/* Motion system of the CRM (spec docs/superpowers/specs/2026-10-13-crm-fluessig-design.md, 13.10.2026).
   Loaded as the last stylesheet in index.html. It holds the motion tokens and the few cross-cutting motion rules;
   component transitions use the tokens where they are declared (crm-style.css, crm-shell.css, crm-workspace.css).
   Rules: only opacity, transform, background-color, border-color, color and box-shadow animate. Never
   "transition: all", never layout properties, no endless animations, no overshoot or spring curves.
   Entering uses --ease-out, leaving --ease-in and is about 30 % shorter. */
:root {
    --motion-fast: 120ms;        /* colour, border, hover, focus */
    --motion-base: 140ms;        /* tab and view switch (opacity fade) */
    --motion-panel: 180ms;       /* toast, toggle, entry of a new card */
    --motion-dialog: 200ms;      /* desktop dialog in */
    --motion-slow: 240ms;        /* phone sheet and sidebar in */
    --motion-press: 100ms;       /* press feedback on primary buttons */
    --motion-base-out: 100ms;
    --motion-panel-out: 120ms;
    --motion-slow-out: 160ms;    /* dialog, sheet and sidebar out */
    --ease-out: cubic-bezier(.2, .8, .2, 1);
    --ease-in: cubic-bezier(.4, 0, 1, 1);
    --ease-standard: cubic-bezier(.2, 0, 0, 1);
}

/* Defined once. Used by inline styles in crm-script.js and index.html (quick note, won and lost dialog, other
   small dialogs), which until now referenced keyframes that did not exist and popped up without motion. */
@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}
@keyframes modalIn {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: none; }
}
/* Leaving counterparts (spec: about 30 % shorter, --ease-in), used with fill-mode forwards. */
@keyframes fadeOut {
    to { opacity: 0; }
}
@keyframes modalOut {
    to { opacity: 0; transform: translateY(8px); }
}

/* Small dialogs (crmConfirm, hint popup, the confirmations crm-script.js builds as .workspace-overlay, quick note,
   won and lost dialog): the scrim fades in and the card rises 8 px, 200 ms. The won and lost dialog replay it on
   every display:none -> flex. :where() keeps the specificity at zero, so the source rules (quick note) and the
   leaving picture below win. */
:where(.workspace-overlay, .hinweis-popup, .quick-note-overlay, #won-modal, #lost-modal) {
    animation: fadeIn var(--motion-dialog) var(--ease-out) backwards;
}
:where(.workspace-overlay, .hinweis-popup, .quick-note-overlay, #won-modal, #lost-modal) > :not(.toast) {
    animation: modalIn var(--motion-dialog) var(--ease-out) backwards;
}

/* Leaving picture of a small dialog (dialogAusblenden in crm-script.js): the dialog itself is already closed and
   gone for the app; this inert copy without ids fades out in 160 ms and removes itself. */
.dialog-ausblenden {
    animation: fadeOut var(--motion-slow-out) var(--ease-in) forwards;
    pointer-events: none;
}
.dialog-ausblenden > :not(.toast) {
    animation: modalOut var(--motion-slow-out) var(--ease-in) forwards;
}

/* Native modal dialogs: 200 ms in. Arbeitsplanung and profile also leave in 160 ms: their JS sets .is-closing
   and calls close() when the animation ends. */
:where(.ap-dialog, .profile-dialog, .crm-meeting-dialog, .crm-drive-dialog, .agent-deal, .customer-task-dialog)[open] {
    animation: modalIn var(--motion-dialog) var(--ease-out) backwards;
}
:where(.ap-dialog, .profile-dialog).is-closing {
    animation: modalOut var(--motion-slow-out) var(--ease-in) forwards;
    pointer-events: none;
}
/* The global reduced-motion rule (crm-style.css) covers *, ::before and ::after, not ::backdrop. */
@media (prefers-reduced-motion: no-preference) {
    :where(.ap-dialog, .profile-dialog, .crm-meeting-dialog, .crm-drive-dialog, .agent-deal, .customer-task-dialog)[open]::backdrop {
        animation: fadeIn var(--motion-dialog) var(--ease-out) backwards;
    }
    :where(.ap-dialog, .profile-dialog).is-closing::backdrop {
        animation: fadeOut var(--motion-slow-out) var(--ease-in) forwards;
    }
}

/* Scroll lock behind every open overlay (spec: no background scroll jumps). The document is the scroller and the
   overlays are fixed, so a wheel turn or a swipe on a scrim scrolled the page behind it, and after closing the page
   stood somewhere else. :has() needs no JS bookkeeping; the scroll position is kept. The gutter stays reserved
   while locked, so a classic scrollbar does not shift the page by its width. Won and lost dialog: class set by JS. */
html:has(> body > :is(.modal-overlay.open, .workspace-overlay, .hinweis-popup, .quick-note-overlay, .crm-overlay-open)),
html:has(dialog:modal) {
    overflow-y: hidden;
    scrollbar-gutter: stable;
}

/* Toasts are fixed and never move the layout. A toast replacing a visible one fades its new text in, instead of
   the box snapping to the new width (showToast). The Arbeitsplanung toast fades in and out (arbeitsplanung.js). */
#toast.toast.toast-wechsel {
    animation: fadeIn var(--motion-fast) var(--ease-out);
}
.ap-toast:not([hidden]) {
    animation: fadeIn var(--motion-panel) var(--ease-out) backwards;
}
.ap-toast.is-leaving {
    animation: fadeOut var(--motion-panel-out) var(--ease-in) forwards;
}

/* Real tab or view switch only, never a re-render of the same data: 140 ms opacity fade without transform,
   so sticky children and the layout stay put. The caller adds the class and removes it on animationend. */
.view-enter {
    animation: fadeIn var(--motion-base) var(--ease-standard) backwards;
}

/* Only a card that is really new on the board (created or just moved) fades in; a re-render leaves the
   cards alone. fill-mode backwards holds no opacity after the end, so the drag placeholder (opacity .35)
   needs no workaround. */
.kanban-card.is-new {
    animation: fadeIn var(--motion-panel) var(--ease-out) backwards;
}

/* Press feedback on primary buttons only (the transform transition is declared with the button). */
.btn-flat-primary:active:not(:disabled),
.btn-add-lead:active:not(:disabled) {
    transform: scale(.98);
}

/* Light/dark switch: one clean swap instead of a wave of several hundred colour transitions.
   toggleTheme is meant to set the class around the switch and drop it after two animation frames (JS hook,
   not wired yet in step 1). !important is required: it has to beat the transitions that
   crm-script.js writes into inline style attributes and the existing !important transitions. */
html.theme-switching *,
html.theme-switching *::before,
html.theme-switching *::after {
    transition: none !important;
}

/* Touch screens: no backdrop blur behind overlays (a full-screen backdrop pass per frame is expensive on
   phones), a slightly darker scrim instead. Desktop keeps a blur of at most 4 px (set at the source). */
@media (pointer: coarse) {
    body :is(.modal-overlay, .cc-modal-hg, .workspace-overlay, .sidebar-overlay) {
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
    }
    body .modal-overlay { background: rgba(15, 23, 42, 0.6); }
    body :is(.cc-modal-hg, .sidebar-overlay) { background: rgba(0, 0, 0, 0.6); }
    :is(.ap-dialog, .profile-dialog, .crm-meeting-dialog, .crm-drive-dialog)::backdrop {
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
    }
    .ap-dialog::backdrop { background: #05080ec4; }
    :is(.profile-dialog, .crm-meeting-dialog)::backdrop { background: #000000b3; }
    .crm-drive-dialog::backdrop { background: #0009; }
    /* !important: these overlays are built by crm-script.js and index.html with an inline backdrop-filter;
       only !important beats an inline style. Their inline scrims are already dark (0.5 to 0.65). */
    [style*="backdrop-filter"] {
        -webkit-backdrop-filter: none !important;
        backdrop-filter: none !important;
    }
}

@media (prefers-reduced-motion: reduce) {
    .view-enter,
    .kanban-card.is-new {
        animation: none;
    }
}
