/* Dichte 1:1 zur Referenz: Root bleibt 16px (rem-basierte Groessen wie text-2xl/h-9/Paddings/
   Sidebar treffen damit exakt die Referenz). Der geerbte Default-Text wird auf 14px gesetzt -
   so sind Nav/Controls kompakt wie das text-sm der Referenz, Ueberschriften behalten ihre rem-Groesse.
   (Referenz: 16px-Root + text-sm an den Komponenten.) */
body {
    font-size: 0.875rem; /* 14px */
    font-family: inherit;
}

/* Fixed-Shell-SPA: die Shell (Sidebar + Header) darf NIE mitscrollen - jede Seite scrollt in
   ihren eigenen Containern. Ohne diese Verriegelung konnte transienter Ueberlauf (z.B. beim
   Aufklappen eines Accordions im Design-Studio) den body scrollen und die ganze Shell verschieben. */
html, body {
    height: 100%;
    overflow: hidden;
    overscroll-behavior: none;
}

/* Lumeo-Feintuning (docs/lumeo-feedback.md, Wunsch): der Overlay-Backdrop nutzt das generische
   animate-fade-out (preview.14: 0.15s, symmetrisch zum Einblenden), das Sheet slidet aber 0.3s -
   der Backdrop ist visuell deutlich frueher weg als das Panel ("erst Backdrop, dann Sheet").
   Dauer + Kurve hier aufs Panel angeglichen, eng auf das Backdrop-Div gescoped (ungelayert NACH
   lumeo.css noetig, sonst verliert die Regel). Entfernen, sobald der Backdrop die Panel-Dauer
   trackt. Bei Lumeo-Updates: Slide-Dauer in lumeo.css pruefen und hier nachziehen. */
div[style*="--color-overlay-backdrop"].animate-fade-out {
    animation-duration: 0.16s;
    animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}

/* RUCK BEIM LANGSAMEN WEGWISCHEN (Mike-Meldung 11.09.2026: "wenn ich es langsam swipe und dann
   loslasse, schliesst es sich zwar, aber davor snapt es kurz wieder auf volle Breite").

   Ursache liegt in Lumeo: die Ausblend-Animation ist ein Keyframe von translateX(0) bis ganz raus.
   Beim Loslassen wird die Zieh-Transformation entfernt und die Animation gestartet - sie beginnt
   also IMMER bei "ganz offen", egal wie weit man schon geschoben hatte. Bei einer schnellen
   Wischbewegung steht das Panel ohnehin fast am Rand, der Sprung ist winzig; wischt man langsam bis
   zur Mitte, springt es sichtbar zurueck und faehrt dann erst raus.

   Von aussen laesst sich der Startpunkt einer Keyframe-Animation nicht verschieben, und abschalten
   duerfen wir sie nicht: Lumeo raeumt das Overlay erst beim animationend-Ereignis ab - ohne
   Animation bliebe es unsichtbar im DOM stehen und wuerde Klicks abfangen.

   Also verkuerzt statt abgeschafft: bei 0,16s ist der Rucksprung nach ein bis zwei Bildern vorbei
   und faellt nicht mehr auf. 0,16s liegt zugleich im ueblichen Rahmen fuer Ausblend-Bewegungen
   (Material: 150-200ms), wirkt also nicht gehetzt. Die Backdrop-Dauer oben ist mitgezogen, sonst
   liefe genau die Ungleichheit wieder auf, die dieser Block eigentlich behebt.

   Betrifft nur Sheets und Drawer - Dialoge blenden mit zoom-out aus, nicht mit slide-out.
   Wegnehmen, sobald Lumeo die Animation beim Startpunkt der Zieh-Position beginnen laesst. */
[class*="animate-slide-out-to-"] {
    animation-duration: 0.16s !important;
}

/* Onboarding-/Hint-Spotlight: pulsierender Ring um das hervorgehobene Element. Mit ki erstellt. */
@keyframes tour-pulse {
    0%, 100% {
        opacity: 0.4;
        transform: scale(1);
    }
    50% {
        opacity: 0.8;
        transform: scale(1.015);
    }
}

/* Empty-State-Dashboard: sanftes Einblenden (Ersatz fuer das Framer-Motion-Stagger der Referenz). */
@keyframes welcome-in {
    from {
        opacity: 0;
        transform: translateY(12px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Bento-Karten: Entrance (Ersatz fuer Framer-Motion y:20->0, scale:0.95->1, 0.4s easeOut). */
@keyframes bento-in {
    from {
        opacity: 0;
        transform: translateY(20px) scale(0.95);
    }
    to {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

/* Unsaved-Changes-Bar (Discord-Muster): mobile Sonderposition ueber der fixierten MobileBottomNav
   (4rem + Safe-Area, vgl. .adepto-mobile-navpad). Basis-Position (bottom:1.5rem, Desktop + Fallback)
   und der Einblend-Keyframe (unsaved-bar-in) zogen nach Adepto.UI.Shell/wwwroot/css/shell.css um
   (Portal-Savebar-Etappe: die Bar/der Tracker sind jetzt eine geteilte Shell-Komponente, siehe dortiger
   Kommentar) - NUR dieser Mobile-Override bleibt hier, weil ausschliesslich die Kunden-App eine
   MobileBottomNav hat, die verdeckt werden muesste (das Admin-Portal hat keine, bleibt also auch
   mobil auf der Shell-Basisposition). Als eigene Klasse statt bottom-[calc(...)]-Utility, weil die
   Tailwind-CLI die calc()+env()-Kombination nicht generiert (gleiche Falle wie bei .adepto-mobile-navpad). */
@media (max-width: 1023.98px) {
    .adepto-unsaved-bar {
        bottom: calc(var(--adepto-bottomnav-h) + env(safe-area-inset-bottom) + 0.5rem);
    }
}

/* Listen-Items (Recent/Expiring): von links einrutschen (Framer x:-20->0, 0.3s). */
@keyframes list-item-in {
    from {
        opacity: 0;
        transform: translateX(-20px);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .animate-\[bento-in_0\.4s_ease-out_both\],
    .animate-\[list-item-in_0\.3s_ease-out_both\] {
        animation: none !important;
    }
}

/* Lumeo Select-Popover hat keine Default-Max-Höhe -> lange Listen (z.B. Kategorien) werden höher
   als der Viewport und lassen sich nicht scrollen. Auf Viewport begrenzen + intern scrollen. */
[id^="select-content"] {
    max-height: min(60vh, 22rem);
    overflow-y: auto;
}

/* Lumeo Select Multiple="true": der Trigger hat eine FESTE Hoehe (h-8/h-9 je nach Density), waechst aber
   nicht mit, wenn die Tags auf zwei Zeilen umbrechen - der Tag-Container ueberragt die Box dann symmetrisch
   nach oben UND unten (zentriertes Overflow durch items-center), wodurch das Chevron/Clear-Icon mittig
   "in der Luft" zwischen den Zeilen haengt statt an der ersten Zeile auszurichten. Fix: Trigger auf
   natuerliche Hoehe (waechst mit den Zeilen) + oben ausrichten statt zentrieren (siehe docs/lumeo-feedback.md B6).
   WICHTIG (Dichte-Regression behoben): min-height war hier fix auf 2.25rem/36px verdrahtet - das war
   die comfortable-Hoehe und hat bei Density.Compact (AppSelect setzt h-8/32px) IMMER 36px erzwungen,
   min-height schlaegt jede kleinere Hoehe. Ergebnis: Auswahlfelder blieben trotz Kompakt-Umstellung bei
   36px, waehrend Input/NumberInput daneben auf 32px sanken (gemessen: 36px vs. 32px, doppelt so
   auffaelliger Bruch wie vor der Umstellung). Der Trigger traegt die Lumeo-Hoehenklasse (h-8 compact /
   h-9 comfortable) bereits selbst im Markup - min-height jetzt je Klasse gesetzt statt pauschal, damit
   der urspruengliche Multi-Line-Fix in BEIDEN Dichten greift, aber die Compact-Dichte nicht mehr
   uebersteuert wird. */
[id^="select-trigger"] {
    height: auto;
    align-items: flex-start;
}
[id^="select-trigger"].h-8 {
    min-height: 2rem; /* 32px, Density.Compact - deckt sich mit h-8s eigener Zielhoehe */
}
[id^="select-trigger"].h-9 {
    min-height: 2.25rem; /* 36px, Density.Comfortable (Default) - unveraendert wie zuvor */
}

[id^="select-trigger"] > .ms-auto.shrink-0 {
    padding-top: 0.125rem;
}

/* --- Filter-Drawer (Mobile): konstante Hoehe statt ruckartigem Resize beim Drill-in --- */
/* Die Filter-Untermenues (Format/Kategorie/Max-Preis ...) sind unterschiedlich hoch. Ohne feste Hoehe
   springt der Bottom-Drawer beim Wechsel ins Untermenue instant groesser/kleiner. Feste Hoehe + internes
   Scrollen haelt den Sheet stabil; hoch genug fuer die laengste Standard-View, Rest scrollt. */
.adepto-filter-drawer-body {
    height: 21rem;
    max-height: 60vh;
}

/* --- Toolbar-Action-Buttons: gleich grosse Quadrate auf dem Phone --- */
/* Unter sm (640px) blenden die Header-Buttons ihr Text-Label aus und zeigen nur das Icon.
   Ohne feste Breite ergeben unterschiedliche Icon-Groessen/Paddings ungleich breite Buttons.
   Mike-Fund 15.09.2026: hier stand vorher eine HARTCODIERTE Breite (2rem), an die urspruengliche
   Lumeo-Sm-Hoehe (h-8) angelehnt - unabhaengig von --lumeo-control-h-sm. Waehrend der Touch-Etappe
   stand --lumeo-control-h-sm zwischenzeitlich auf 2.75rem/44px, die Breite blieb bei den
   hartcodierten 2rem/32px stehen -> ein Rechteck statt eines Quadrats; die feste Breite traf nur
   zufaellig, wenn beide Werte gerade uebereinstimmten. Fix: width:auto + aspect-ratio 1/1 - die
   Breite folgt jetzt der tatsaechlichen Hoehe (kommt weiterhin von Lumeo/--lumeo-control-h-sm),
   bleibt quadratisch bei JEDER Groesse, ohne an einer zweiten, separat zu pflegenden Variable zu
   haengen. justify-content:center ist bereits Lumeos eigener Button-Default (items-center
   justify-center, per bUnit-Markup-Dump verifiziert) - hier nur zur Sicherheit gegen kuenftige
   Ueberschreibungen wiederholt. gap:0 + margin:0 am Icon-Kind: das ausgeblendete <span> (display:none)
   zaehlt fuer Flexbox-gap ohnehin nicht als Element, und die vorhandenen Icon-Abstaende sind alle
   "sm:mr-*" (greifen selbst erst ab 640px) - beides also aktuell wirkungslos, aber als Netz gegen
   eine kuenftige NICHT-responsive Abstandsklasse am Icon (sonst saesse die Glyphe im Quadrat links
   statt zentriert). Ab sm greift der ganze Block nicht -> Text + normales Padding kehren
   unveraendert zurueck. */
@media (max-width: 639.98px) {
    .adepto-toolbar-icon-btn {
        width: auto;
        aspect-ratio: 1 / 1;
        padding-inline: 0;
        gap: 0;
        justify-content: center;
    }
    .adepto-toolbar-icon-btn > svg {
        margin: 0;
    }
}

/* --- Mobile-Dichte (Phone): ENTFERNT (Abnahme naechste Etappe) ---
   Frueher hier: @media (max-width: 767.98px) { html { font-size: 15px; } } - eine Kunden-App-exklusive
   Wurzelschrift-Absenkung fuer "kompaktere Visuals wie native Apps". GEMESSEN war das der zweite,
   unbeabsichtigte Unterschied zur Admin-Portal-App (die auf Handys durchgaengig bei 16px-Root bleibt):
   dieselbe Density.Compact-Klasse (rem-basiert) fiel dadurch in der Kunden-App auf Touch-Geraeten NOCH
   kleiner aus als im Portal (30px statt 32px Antippflaeche). Die neue Touch-Mindesthoehe oben in
   tailwind/input.css (min-height: 36px !important, PX statt REM) haengt zwar nicht mehr an der
   Root-Schrift, aber eine Breite-getriebene Schrumpf-Regel widerspricht ohnehin dem Prinzip dieser
   Runde ("kompakt gehoert an die Maus, nicht an den Finger") - ein schmales Desktop-Fenster mit Maus
   wuerde die 15px unveraendert mitbekommen, ein breites Tablet mit Touch nicht. Ersatzlos entfernt,
   beide Apps bleiben jetzt auf jeder Breite bei 16px-Root.
*/

/* Scrollbar ausblenden (z.B. horizontale Tab-Leiste) - swipebar, aber ohne sichtbaren Balken-Track. */
.no-scrollbar {
    -ms-overflow-style: none;
    scrollbar-width: none;
}
.no-scrollbar::-webkit-scrollbar {
    display: none;
}

/* --- Drawer-Bottom-Abstand (Mobile) --- */
/* WICHTIG: NICHT als Tailwind-Arbitrary `pb-[max(2.5rem,calc(env(...)+1.75rem))]` schreiben - das `calc`
   OHNE Leerzeichen um das `+` ist ungueltiges CSS, der Browser verwirft die ganze Deklaration -> 0 Abstand
   (genau der Bug: alle Drawer klebten unten am Rand). Hier valides CSS mit Leerzeichen. */
.adepto-drawer-pad {
    padding-bottom: max(2rem, calc(env(safe-area-inset-bottom) + 1.5rem));
}

/* --- Sidebar-Collapse/Expand: keine eigenen Overrides --- */
/* Seit Lumeo 4.1.0-preview.1 (shadcn-Paritaet) animiert die Sidebar nativ synchron - auch die
   eigenen Hero-Buttons in Header/Footer brauchen nichts mehr (vom Nutzer visuell bestaetigt).
   Falls hier je wieder eine Transition noetig wird: als ungelayerte CSS-Regel, NICHT als
   Tailwind-Utility - Lumeos ungelayerte .lumeo-press-brightness-Regel (transition: filter)
   ueberstimmt jede gelayerte Utility. */

/* --- Sheet-/Dialog-Layout-Fixes --- */

/* OverlayForm-Body hat -mx-1 (Fokusring-Luft); zusammen mit overflow-y-auto erzeugte das einen
   spurious horizontalen Scrollbalken. Horizontal clippen statt scrollen (fixed-Popover liegen
   ausserhalb und werden NICHT geclippt, da der Body kein Containing-Block ist). */
.overflow-y-auto.-mx-1.px-1 {
    overflow-x: clip;
    /* Scrollbalken an den Sheet-Rand legen statt 24px innen: das Lumeo-Shell hat p-6 (24px),
       der scrollende Form-Body sitzt darin. Body rechts bis zum Shell-Rand ziehen
       (margin-right negativ) und den Inhalt per padding-right wieder einruecken. */
    margin-right: -1.5rem;
    padding-right: 1.5rem;
}

/* Lumeo DatePicker schrumpfbar machen: der Trigger ist intrinsisch ~238px breit (inline-flex,
   Input mit min-width:auto) und sprengt schmale 2-Spalten-Grids (Datum-Felder ueberlappten/
   ueberliefen den Sheet-Rand). Trigger auf volle Spaltenbreite + Input darf unter Inhaltsbreite. */
/* WICHTIG: Lumeo legt die DatePicker-`Class` auf den TRIGGER (border-input-Element), nicht den Root.
   Also NICHT display setzen (Trigger muss flex bleiben, sonst stapeln Input + Icon untereinander),
   nur schrumpfbar machen. Die content-sizenden Wrapper (inline-flex + inline-block-Root) per :has()
   auf volle Spaltenbreite ziehen, damit der Trigger (hat w-full) die Spalte füllt statt zu überlaufen. */
.adepto-dp {
    min-width: 0;
}

.adepto-dp input {
    min-width: 0;
}

.inline-flex:has(> .adepto-dp) {
    display: flex;
    width: 100%;
    min-width: 0;
}

.inline-block:has(> .inline-flex > .adepto-dp) {
    display: block;
    width: 100%;
}


/* AccountOrg-Drawer: Drag-Handle dezenter (wie Referenz: duenner, schmaler, halbtransparent statt bg-muted). */
.mx-auto.mt-4.h-2.w-\[100px\].rounded-full.bg-muted {
    /* Absolute px (kein rem) + min-height + flex-shrink:0, weil der Handle als Flex-Kind
       sonst auf ~4px gestaucht wurde. Referenz: h-1.5 = 6px (dicker als vorher). */
    height: 6px !important;
    min-height: 6px !important;
    flex-shrink: 0;
    width: 48px;                   /* w-12 */
    /* Referenz: Handle in py-3-Wrapper -> 12px ueber UND unter dem Handle. */
    margin-top: 0.75rem;
    margin-bottom: 0.75rem;
    background-color: color-mix(in oklch, var(--color-muted-foreground) 30%, transparent);
}

/* AccountOrg-Bottom-Drawer: Hoehe hart auf 75dvh begrenzen + scrollbar. Lumeos DrawerContent setzt
   max-h-[96vh], das sonst (an kurzen Viewports) fast den ganzen Screen fuellt. !important gewinnt. */
.fixed.inset-x-0.bottom-0[class*="rounded-t-"] {
    max-height: 75dvh !important;
    /* overflow hidden am Drawer selbst, damit der Drag-Handle (Lumeo-Kind, oben) NICHT
       mitscrollt; der Inhalt scrollt in einem inneren Wrapper (wie die Referenz). */
    overflow: hidden;
    /* Lumeo merged das px-4 der DrawerContent-Class weg -> Inhalt klebte am Rand.
       Referenz: Container px-4 (16px). Erzwingen, damit der Inhalt Luft zum Rand hat. */
    padding-left: 1rem !important;
    padding-right: 1rem !important;
}

/* Im Drawer den geerbten Default-Text auf volle 16px setzen (App-weit ist er auf 14px gedrueckt).
   Der Referenz-Drawer nutzt 16px-Text -> groessere, touch-freundliche Menuezeilen/Tabs. */
.fixed.inset-x-0.bottom-0[class*="rounded-t-"] {
    font-size: 1rem;
}

/* Mobile Add/Edit-Credential-Sheet (Marker via OverlayPresets.CredentialSheet):
   Lumeos MobileFullscreen greift hier nicht zuverlaessig -> Vollbild hart erzwingen.
   Und den Shell-Schliessen-X ausblenden: die Komponente rendert stattdessen einen eigenen
   Zurueck-Pfeil (1:1 wie der Referenz-Drawer direction="right": Pfeil oben links, kein X). */
.adepto-credential-sheet {
    width: 100% !important;
    max-width: 100% !important;
    height: 100% !important;
    max-height: 100% !important;
    border-radius: 0 !important;
}
.adepto-credential-sheet [aria-label="Close"] {
    display: none !important;
}

/* (Mobile-Dialog-Hacks via JS/CSS wieder entfernt - der Nutzer will echte Drawer/Sheets statt
   einen Dialog per Script/CSS zu missbrauchen. Mobile Dialoge werden auf echte Lumeo-Drawer umgestellt.) */

/* ── Onboarding-Karussell: Mini-Mockup-Motion ───────────────────────────────
   Gestaffeltes Einblenden (ob-rise), Pop beim Erscheinen (ob-pop), dezentes
   Schweben (ob-float). Die Slides remounten per @key -> Animation spielt je Slide neu. */
@keyframes ob-rise {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: translateY(0); }
}
.ob-rise { animation: ob-rise .5s ease-out both; }

@keyframes ob-pop {
    0%   { opacity: 0; transform: scale(.85); }
    60%  { transform: scale(1.04); }
    100% { opacity: 1; transform: scale(1); }
}
.ob-pop { animation: ob-pop .5s ease-out both; }

@keyframes ob-float {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-4px); }
}
.ob-float { animation: ob-float 2.5s ease-in-out infinite; }

/* ── Onboarding-Karussell: Cursor-"Create"-Demo ─────────────────────────────
   Ein 3s-Loop: Cursor faehrt zum Button (--ox/--oy = Start-Offset), klickt
   (Squish), ein Ripple geht auf, danach erscheint das neue Element (ob-result).
   Alle drei teilen die 3s-Dauer -> synchron. */
.ob-cursor {
    position: absolute;
    pointer-events: none;
    z-index: 10;
    color: var(--color-foreground, #111827);
    filter: drop-shadow(0 1px 2px rgba(0,0,0,.4));
    animation: ob-cursor 3s ease-in-out infinite;
}
@keyframes ob-cursor {
    0%   { opacity: 0; transform: translate(var(--ox, 26px), var(--oy, 28px)) scale(1); }
    15%  { opacity: 1; }
    45%  { transform: translate(0,0) scale(1); }
    52%  { transform: translate(0,0) scale(.82); }
    62%  { transform: translate(0,0) scale(1); }
    85%  { opacity: 1; transform: translate(0,0) scale(1); }
    100% { opacity: 0; transform: translate(var(--ox, 26px), var(--oy, 28px)) scale(1); }
}
.ob-ripple { animation: ob-ripple 3s ease-out infinite; }
@keyframes ob-ripple {
    0%, 50% { opacity: 0; transform: scale(0); }
    55%     { opacity: .45; transform: scale(1); }
    72%     { opacity: 0; transform: scale(1.9); }
    100%    { opacity: 0; }
}
.ob-result { animation: ob-result 3s ease-out infinite; }
@keyframes ob-result {
    0%, 50%  { opacity: 0; transform: translateY(7px); }
    60%, 90% { opacity: 1; transform: translateY(0); }
    100%     { opacity: 0; transform: translateY(7px); }
}
