/* ============================================================
   Motion stage — staged page entrance for the Comercial section
   ============================================================

   Opt-in: a page adds `motion-stage` to its root element. Today only the
   Comercial section pages (/comercial/*) do, so nothing else in the hub is
   affected — the class is the whole scoping mechanism.

   The choreography follows the reading order of every screen in the section:

     1. chrome     tabs, toolbars, filter bars, section titles
     2. cards      KPI cards, staggered across the row
     3. charts     doughnut cards, staggered across the grid
     4. surfaces   tables, lists, panels, kanban columns
     5. rows       the first rows inside those surfaces
     +  modals     the section's detail dialogs, which had no entrance at all

   Two decisions are worth knowing before editing this file.

   ENTRANCE IS TRIGGERED BY INSERTION, NOT BY A TIMER. A CSS animation runs
   when the element enters the DOM, which is what makes this work in Blazor:
   the shell renders first and the data arrives seconds later, so a fixed
   timeline would run out against an empty page. Each block animates when it
   actually appears — and a filter change replays nothing, because Blazor
   reuses the existing rows and reused DOM does not restart an animation.

   EVERY DURATION AND DELAY COMES FROM THE --ms-* TOKENS. That is what keeps
   the reduced-motion block at the end a handful of variable overrides instead
   of an `!important` sweep: neutralising the tokens neutralises this file and
   leaves every other animation in the app (loaders, .skel shimmer, the Agenda
   calendar carousel) exactly as it was.
*/

.motion-stage {
    /* Same curve as the Agenda calendar carousel, so the section reads as one
       system: fast out of the gate, long settle, no overshoot. */
    --ms-ease: cubic-bezier(.22, 1, .36, 1);

    --ms-quick: 240ms;
    --ms-base: 320ms;
    --ms-slow: 400ms;

    /* Stagger between siblings. Rows get their own, much shorter step: a table
       is a dozen items, a card row is four. */
    --ms-step: 45ms;
    --ms-step-row: 18ms;

    /* Travel. Small on purpose — the movement should register as arrival, not
       as a slide. */
    --ms-rise: 10px;
    --ms-rise-sm: 4px;

    /* Lead-in per layer, so the layers overlap rather than queue. */
    --ms-lead-card: 40ms;
    --ms-lead-chart: 60ms;
    --ms-lead-surface: 60ms;
    --ms-lead-row: 100ms;
    --ms-lead-value: 90ms;
}

/* ── Keyframes ───────────────────────────────────────────────
   All of them end at `transform: none` rather than `translateY(0)`: a leftover
   transform would make the element a containing block forever, which quietly
   breaks `position: sticky` and `position: fixed` descendants. */
@keyframes ms-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes ms-rise {
    from { opacity: 0; transform: translateY(var(--ms-rise)); }
    to   { opacity: 1; transform: none; }
}

@keyframes ms-rise-sm {
    from { opacity: 0; transform: translateY(var(--ms-rise-sm)); }
    to   { opacity: 1; transform: none; }
}

@keyframes ms-lift {
    from { opacity: 0; transform: translateY(var(--ms-rise)) scale(.985); }
    to   { opacity: 1; transform: none; }
}

@keyframes ms-modal {
    from { opacity: 0; transform: translateY(10px) scale(.985); }
    to   { opacity: 1; transform: none; }
}

/* ── Sibling index ───────────────────────────────────────────
   Every stagger here is `lead + index * step`, so an element's position among
   its siblings is the only per-element value. Declaring it once — instead of a
   delay rule per layer per position — keeps each layer below to a single rule.
   It inherits on purpose: .kpi-card-value picks up the index of the card it
   sits in and lands just after it.

   :where() zeroes the specificity of the container list, so these rules stay
   at (0,2,0) and the modal reset further down can outrank the layers. */
.motion-stage :where(.kpi-cards-row, .doc-coverage-grid, .kanban-board, .agenda-sidebar,
                     .ativosalt-feed-strip, .kpi-table > tbody, .cr-table > tbody,
                     .bday-day-list, .cr-pendentes-list) > :nth-child(1)  { --ms-i: 0; }
.motion-stage :where(.kpi-cards-row, .doc-coverage-grid, .kanban-board, .agenda-sidebar,
                     .ativosalt-feed-strip, .kpi-table > tbody, .cr-table > tbody,
                     .bday-day-list, .cr-pendentes-list) > :nth-child(2)  { --ms-i: 1; }
.motion-stage :where(.kpi-cards-row, .doc-coverage-grid, .kanban-board, .agenda-sidebar,
                     .ativosalt-feed-strip, .kpi-table > tbody, .cr-table > tbody,
                     .bday-day-list, .cr-pendentes-list) > :nth-child(3)  { --ms-i: 2; }
.motion-stage :where(.kpi-cards-row, .doc-coverage-grid, .kanban-board, .agenda-sidebar,
                     .ativosalt-feed-strip, .kpi-table > tbody, .cr-table > tbody,
                     .bday-day-list, .cr-pendentes-list) > :nth-child(4)  { --ms-i: 3; }
.motion-stage :where(.kpi-cards-row, .doc-coverage-grid, .kanban-board, .agenda-sidebar,
                     .ativosalt-feed-strip, .kpi-table > tbody, .cr-table > tbody,
                     .bday-day-list, .cr-pendentes-list) > :nth-child(5)  { --ms-i: 4; }
.motion-stage :where(.kpi-cards-row, .doc-coverage-grid, .kanban-board, .agenda-sidebar,
                     .ativosalt-feed-strip, .kpi-table > tbody, .cr-table > tbody,
                     .bday-day-list, .cr-pendentes-list) > :nth-child(6)  { --ms-i: 5; }
.motion-stage :where(.kpi-table > tbody, .cr-table > tbody, .bday-day-list,
                     .cr-pendentes-list, .ativosalt-feed-strip) > :nth-child(7)  { --ms-i: 6; }
.motion-stage :where(.kpi-table > tbody, .cr-table > tbody, .bday-day-list,
                     .cr-pendentes-list, .ativosalt-feed-strip) > :nth-child(8)  { --ms-i: 7; }
.motion-stage :where(.kpi-table > tbody, .cr-table > tbody, .bday-day-list,
                     .cr-pendentes-list, .ativosalt-feed-strip) > :nth-child(9)  { --ms-i: 8; }
.motion-stage :where(.kpi-table > tbody, .cr-table > tbody, .bday-day-list,
                     .cr-pendentes-list, .ativosalt-feed-strip) > :nth-child(10) { --ms-i: 9; }
.motion-stage :where(.kpi-table > tbody, .cr-table > tbody, .bday-day-list,
                     .cr-pendentes-list, .ativosalt-feed-strip) > :nth-child(11) { --ms-i: 10; }
.motion-stage :where(.kpi-table > tbody, .cr-table > tbody, .bday-day-list,
                     .cr-pendentes-list, .ativosalt-feed-strip) > :nth-child(12) { --ms-i: 11; }

/* ── 1 · Chrome ──────────────────────────────────────────────
   Tabs, toolbars and section titles are the frame the content lands inside, so
   they arrive first and together — staggering the frame would read as noise. */
.motion-stage :is(.cmp-tabs, .agenda-toolbar, .kpi-table-filter, .section-title) {
    animation: ms-rise var(--ms-base) var(--ms-ease) both;
}

/* ── 2 · Cards ───────────────────────────────────────────────
   The KPI row is the first thing read on most of these screens, so it gets the
   only scale in the system — a card that grows into place draws the eye. */
.motion-stage .kpi-cards-row > * {
    animation: ms-lift var(--ms-base) var(--ms-ease) both;
    animation-delay: calc(var(--ms-lead-card) + var(--ms-i, 0) * var(--ms-step));
}

/* The number settles a beat after its own card. On these pages the card shell
   renders as a skeleton and the value is inserted later, when Dataverse
   answers — so this fires on the real number, not on the placeholder. */
.motion-stage .kpi-card-value {
    animation: ms-rise-sm var(--ms-quick) var(--ms-ease) both;
    animation-delay: calc(var(--ms-lead-card) + var(--ms-i, 0) * var(--ms-step) + var(--ms-lead-value));
}

/* ── 3 · Charts ──────────────────────────────────────────────
   Slower than the cards: the doughnuts have their own Chart.js draw animation
   underneath, and a fast card entrance on top of it reads as two things
   happening at once. */
.motion-stage .doc-chart-card {
    animation: ms-rise var(--ms-slow) var(--ms-ease) both;
    animation-delay: calc(var(--ms-lead-chart) + var(--ms-i, 0) * var(--ms-step));
}

/* ── 4 · Surfaces ────────────────────────────────────────────
   Tables, lists, panels and kanban columns. `.card` is the generic wrapper the
   section uses around tables, so it belongs here — minus the two card flavours
   that are their own layer. The :not() lives inside :where() to keep this rule
   at (0,2,0), below the modal reset at the bottom of the file. */
.motion-stage :is(.kpi-table-wrap, .cr-card, .bday-day-list, .ativosalt-feed-strip,
                  .card:where(:not(.doc-chart-card):not(.kpi-card))) {
    animation: ms-rise var(--ms-base) var(--ms-ease) both;
    animation-delay: var(--ms-lead-surface);
}

/* Columns and sidebar cards sit side by side, so they sweep instead of
   arriving at once. Kanban cards themselves are deliberately left alone: they
   are drag targets, and re-animating one as it lands in a new column would
   fight the drag it just finished. */
.motion-stage :is(.kanban-board > .kanban-col, .agenda-sidebar > *) {
    animation: ms-rise var(--ms-base) var(--ms-ease) both;
    animation-delay: calc(var(--ms-lead-surface) + var(--ms-i, 0) * var(--ms-step));
}

/* ── 5 · Rows ────────────────────────────────────────────────
   Only the first twelve. Past that the rows are below the fold, and making row
   40 wait 800 ms to exist buys nothing — it just delays the scroll. */
.motion-stage :is(.kpi-table, .cr-table) > tbody > tr:nth-child(-n+12),
.motion-stage :is(.bday-day-list, .cr-pendentes-list, .ativosalt-feed-strip) > *:nth-child(-n+12) {
    animation: ms-rise-sm var(--ms-quick) var(--ms-ease) both;
    animation-delay: calc(var(--ms-lead-row) + var(--ms-i, 0) * var(--ms-step-row));
}

/* ── Modals ──────────────────────────────────────────────────
   The section's dialogs — client detail, prospect form, chart drill-down —
   had no entrance of their own: they appeared complete in a single frame,
   which on a full-screen client record reads as a jump cut. The backdrop
   fades, the card rises into it. (.bday-modal-* already ships its own
   `bday-pop`, so it is left out.) */
.motion-stage :is(.pv-modal-backdrop, .chart-modal-backdrop, .enq-modal-backdrop) {
    animation: ms-fade var(--ms-quick) ease-out both;
}

.motion-stage :is(.pv-modal, .chart-modal-card, .enq-modal-card) {
    animation: ms-modal var(--ms-base) var(--ms-ease) both;
}

/* A modal is its own entrance, so the page choreography must not replay inside
   it — without this, opening a client detail staggers its tables and rows in
   behind the modal's own rise, and the dialog reads as a second page load.
   Specificity (0,3,0) clears every layer above, and only the names this file
   introduces are reset. */
.motion-stage :is(.pv-modal-backdrop, .chart-modal-backdrop, .enq-modal-backdrop, .bday-modal-backdrop)
    :is(.kpi-cards-row > *, .kpi-card-value, .doc-chart-card, .card, .kpi-table-wrap,
        .kpi-table-filter, .cr-card, .section-title, .cmp-tabs, .agenda-toolbar,
        .kpi-table > tbody > tr, .cr-table > tbody > tr, .bday-day-list, .bday-day-list > *) {
    animation: none;
}

/* ── Reduced motion ──────────────────────────────────────────
   Movement out, cross-fade kept: a fade is the one transition that stays
   comfortable with vestibular sensitivity, and dropping the entrance entirely
   would make late-arriving Dataverse content pop in with no signal at all.
   Overriding the tokens reaches this file and only this file. */
@media (prefers-reduced-motion: reduce) {
    .motion-stage {
        --ms-quick: 120ms;
        --ms-base: 120ms;
        --ms-slow: 120ms;
        --ms-step: 0ms;
        --ms-step-row: 0ms;
        --ms-rise: 0px;
        --ms-rise-sm: 0px;
        --ms-lead-card: 0ms;
        --ms-lead-chart: 0ms;
        --ms-lead-surface: 0ms;
        --ms-lead-row: 0ms;
        --ms-lead-value: 0ms;
    }

    /* The only travel not expressed as a token. */
    @keyframes ms-modal {
        from { opacity: 0; }
        to   { opacity: 1; }
    }
}
