/* ==========================================================================
   REVENTO — MOTION UTILITIES
   ============================================================================
   Motion system Revento: jedna krzywa ruchu (emphasized decel), jeden kierunek
   (lewo→prawo / góra→dół jako "forward"), trzy warstwy czasowe:

     ui   (280ms) — reaktywne: tabs, list tiles, toggles, chips
     page (400ms) — pokazowe: enter transitions całych widoków, staggered
     overlay/data/feedback — dłuższe dla dialogów, wykresów, success/error

   Zawartość:
     - Button sweep (.md-button--sweep) — brand-moment CTA fill-from-left
     - Tabs / nav rail / list item / toggle — indicator sweep
     - Stagger / page enter
     - Overlay (dialog / bottom sheet / side panel)
     - Toast, notification
     - Accordion, focus ring, skeleton, chart enter
     - Success check, error shake, FAB, page transition, tap ripple
   ========================================================================== */

/* --------------------------------------------------------------------------
   BUTTON SWEEP — brand-moment CTA fill-from-left przy hover
   ============================================================================
   Sweep ma SENS tylko przy wysokim kontraście idle→hover. Dlatego:
     - .md-button--sweep na outlined  →  pusto → wypełnia primary (najsilniejsze)
     - .md-button--sweep na tonal     →  secondary-container → primary (średnie)
     - .md-button--sweep na filled    →  primary → deeper primary (słabe, tylko dla marketingu)

   Rekomendacja: używaj na outlined jako default. Tonal kiedy trzeba ciszej.
   Filled+sweep tylko na landingu (hero CTA) gdzie chcesz hover-pokazania marki.

   Zgodne z flow: ruch left→right, 280ms, emphasized decel.
   Użycie świadome — max 2-3 CTA na widok, żeby nie rozwodnić brand-momentu.
   -------------------------------------------------------------------------- */

/* Wspólna baza — wrapper + pigułka, którą animujemy */
.md-button--sweep,
.btn-filled.sweep {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  /* Płynne przejścia koloru tekstu i tła dla idle→hover */
  transition: background-color var(--flow-sweep-duration, 280ms) var(--flow-sweep-ease, cubic-bezier(.2,.8,.2,1)),
              color var(--flow-sweep-duration, 280ms) var(--flow-sweep-ease, cubic-bezier(.2,.8,.2,1)),
              border-color var(--flow-sweep-duration, 280ms) var(--flow-sweep-ease, cubic-bezier(.2,.8,.2,1));
}

/* Pigułka sweep — inset: 0 za tekstem, nad tłem parent */
.md-button--sweep::after,
.btn-filled.sweep::after {
  content: '';
  position: absolute;
  inset: 0;
  min-height: 0; /* reset hit-area trick from base .md-button */
  z-index: 0;
  background: var(--md-primary);
  transform: translateX(-101%);
  transition: transform var(--flow-sweep-duration, 280ms) var(--flow-sweep-ease, cubic-bezier(.2,.8,.2,1));
  border-radius: inherit;
  pointer-events: none;
}
/* Tekst i ikony muszą być nad pigułką sweep */
.md-button--sweep > *,
.btn-filled.sweep > * {
  position: relative;
  z-index: 1;
}
.md-button--sweep:hover::after,
.md-button--sweep:focus-visible::after,
.btn-filled.sweep:hover::after,
.btn-filled.sweep:focus-visible::after {
  transform: translateX(0);
}

/* -- Outlined + sweep: default brand-moment, najsilniejszy kontrast ------ */
.md-button--outlined.md-button--sweep {
  background: transparent;
  color: var(--md-primary);
  border-color: var(--md-outline);
}
.md-button--outlined.md-button--sweep:hover,
.md-button--outlined.md-button--sweep:focus-visible {
  color: var(--md-on-primary);
  border-color: var(--md-primary);
}

/* -- Tonal + sweep: średnie natężenie, dobre dla secondary action -------- */
.md-button--tonal.md-button--sweep {
  background: var(--md-secondary-container);
  color: var(--md-on-secondary-container);
}
.md-button--tonal.md-button--sweep:hover,
.md-button--tonal.md-button--sweep:focus-visible {
  color: var(--md-on-primary);
}

/* -- Filled + sweep: marketing-only, sweep do ciemniejszego primary ------ */
.md-button--filled.md-button--sweep {
  /* Usuwamy obwódkę — na jasnym fille dark sweep ma działać solo */
  border-color: transparent;
}
.md-button--filled.md-button--sweep::after {
  /* Na filled (już primary) — sweep jest ciemniejszą pigułką, kotwicą marki */
  background: var(--secondary-30);
}
.md-button--filled.md-button--sweep:hover,
.md-button--filled.md-button--sweep:focus-visible {
  color: white;
  border-color: transparent;
}

/* ==========================================================================
   SWEEP-WIDE — systemowy język ruchu Revento
   --------------------------------------------------------------------------
   Pigułka (sweep) nie jest ozdobą przycisku CTA — jest motion-motifem marki.
   Wszędzie tam, gdzie użytkownik zmienia kontekst, aktywny element lub
   wybór, widzi ten sam ruch: pigułka wjeżdża od lewej, emphasized decel.

   To daje appce wrażenie PRĘDKOŚCI, o którą nam chodzi — Revento jest szybkie
   nie tylko dlatego że latency jest niska, ale dlatego że KAŻDY ruch UI
   komunikuje tempo. Spójność ruchu = spójność marki.

   Warstwy:
     ui   (280ms) — reaktywne: tabs, list tiles, toggles, chips
     page (400ms) — pokazowe: enter transitions całych widoków, staggered
   ========================================================================== */

/* -- Flow Tab / Segmented Control ---------------------------------------- */
/* Jedna pigułka, która się PRZESUWA pomiędzy tabami.
   Kontener ma jeden .flow-tabs__indicator — JS mierzy width/offset aktywnego
   tab-a i ustawia jego transform + width. Transition na transform + width
   z emphasized-decel 280ms daje wrażenie że pigułka "jedzie" do nowego
   tab-a, nie że dwa oddzielne tła fade'ują.

   Użycie:
     <div class="flow-tabs" role="tablist">
       <span class="flow-tabs__indicator" aria-hidden="true"></span>
       <button class="flow-tab" aria-selected="true"><span>A</span></button>
       <button class="flow-tab"><span>B</span></button>
     </div>
   + JS: po mount i po kliku, ustawia --x i --w na kontenerze.
   Preview zawiera gotowy JS helper.

   BRAND: radius 12px (--md-shape-button), NIE pigułka. Revento nigdzie
   nie używa stadium. */
.flow-tabs {
  --flow-tabs-x: 0;
  --flow-tabs-w: 0;
  position: relative;
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  background: var(--md-surface-container);
  border-radius: 14px;      /* outer shell: button radius + padding */
}
.flow-tabs__indicator {
  position: absolute;
  top: 4px;
  left: 0;
  height: calc(100% - 8px);
  width: var(--flow-tabs-w, 0px);
  transform: translateX(var(--flow-tabs-x, 0px));
  background: var(--md-primary);
  border-radius: var(--md-shape-button);
  transition:
    transform var(--flow-sweep-duration-ui) var(--flow-sweep-ease),
    width     var(--flow-sweep-duration-ui) var(--flow-sweep-ease);
  pointer-events: none;
  z-index: 0;
}
/* Przed pierwszym pomiarem (--flow-tabs-w:0) indicator niewidoczny — nie
   chcemy błysku 0-width primary pill przy mount. */
.flow-tabs[data-ready="false"] .flow-tabs__indicator { opacity: 0; }
.flow-tab {
  position: relative;
  z-index: 1;
  border: 0;
  background: transparent;
  padding: 10px 20px;
  font: var(--md-label-large);
  font-weight: 500;
  color: var(--md-on-surface-variant);
  border-radius: var(--md-shape-button);
  cursor: pointer;
  transition: color var(--flow-sweep-duration-ui) var(--flow-sweep-ease);
  white-space: nowrap;
}
.flow-tab > * { position: relative; z-index: 1; }
.flow-tab[aria-selected="true"] {
  color: var(--md-on-primary);
}
.flow-tab:hover:not([aria-selected="true"]) {
  color: var(--md-on-surface);
}

/* -- Flow Nav Rail / Sidebar -------------------------------------------- */
/* Pionowy wariant — pigułka PRZESUWA się po Y (a nie slide-from-left).
   Ale animacja wejścia indikatora przy mount wchodzi z góry→w dół jak
   "slide in from start". Kierunek jest spójny: zawsze lewa→prawa / góra→dół
   jako "forward" kierunek, żeby ruch mówił o postępie. */
.flow-nav-rail {
  --flow-rail-y: 0;
  --flow-rail-h: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px;
  background: var(--md-surface-container-low);
  border-radius: 14px;
}
.flow-nav-rail__indicator {
  position: absolute;
  left: 8px;
  right: 8px;
  top: 0;
  height: var(--flow-rail-h, 0px);
  transform: translateY(var(--flow-rail-y, 0px));
  background: var(--md-primary);
  border-radius: var(--md-shape-button);
  transition:
    transform var(--flow-sweep-duration-ui) var(--flow-sweep-ease),
    height    var(--flow-sweep-duration-ui) var(--flow-sweep-ease);
  pointer-events: none;
  z-index: 0;
}
.flow-nav-rail[data-ready="false"] .flow-nav-rail__indicator { opacity: 0; }
.flow-nav-item {
  position: relative;
  z-index: 1;
  border: 0;
  background: transparent;
  padding: 12px 16px;
  display: flex;
  align-items: center;
  gap: 12px;
  font: var(--md-label-large);
  font-weight: 500;
  color: var(--md-on-surface-variant);
  border-radius: var(--md-shape-button);
  cursor: pointer;
  text-align: left;
  transition: color var(--flow-sweep-duration-ui) var(--flow-sweep-ease);
}
.flow-nav-item[aria-current="page"] {
  color: var(--md-on-primary);
}
.flow-nav-item:hover:not([aria-current="page"]) {
  color: var(--md-on-surface);
}

/* -- Flow List Item / Tile ---------------------------------------------- */
/* List tile, row, card z nav-action. Na click — pigułka POKRYWA tile
   od lewej (translate 0), tworzy pełne primary wypełnienie, trzyma 120ms,
   potem nav. To eliminuje "pustą klatkę" między kliknięciem a załadowaniem
   nowego widoku — user widzi wyraźne potwierdzenie akcji.

   Żeby to uruchomić, dodaj .flow-list-item--pressed do item.
   JS handler (w preview pokażemy): on click → add .pressed → wait 200ms → nav. */
.flow-list-item {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 20px;
  border: 0;
  background: var(--md-surface-container-low);
  border-radius: var(--md-shape-card);      /* 12px — BRAND */
  cursor: pointer;
  text-align: left;
  width: 100%;
  color: var(--md-on-surface);
  font: var(--md-body-large);
  transition: background var(--flow-sweep-duration-ui) var(--flow-sweep-ease),
              color var(--flow-sweep-duration-ui) var(--flow-sweep-ease);
}
.flow-list-item::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--md-primary);
  border-radius: inherit;
  transform: translateX(-101%);
  transition: transform var(--flow-sweep-duration-ui) var(--flow-sweep-ease);
  z-index: 0;
  pointer-events: none;
}
.flow-list-item > * { position: relative; z-index: 1; }
.flow-list-item:hover {
  background: var(--md-surface-container);
}
.flow-list-item.flow-list-item--pressed {
  color: var(--md-on-primary);
}
.flow-list-item.flow-list-item--pressed::before {
  transform: translateX(0);
}

/* -- Flow Toggle -------------------------------------------------------- */
/* Material switch — thumb w track. Na on, pigułka wypełnia track od lewej
   (nie zwykły color swap). Thumb pozostaje osobno, przesuwa się po track. */
.flow-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  width: 52px;
  height: 32px;
  padding: 4px;
  background: var(--md-surface-container-highest);
  border: 2px solid var(--md-outline);
  border-radius: var(--md-shape-switch-track);   /* 12px — BRAND, nie pill */
  cursor: pointer;
  overflow: hidden;
  transition: border-color var(--flow-sweep-duration-ui) var(--flow-sweep-ease);
}
.flow-toggle::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--md-primary);
  border-radius: inherit;
  transform: translateX(-101%);
  transition: transform var(--flow-sweep-duration-ui) var(--flow-sweep-ease);
  z-index: 0;
  pointer-events: none;
}
.flow-toggle > * { position: relative; z-index: 1; }
.flow-toggle__thumb {
  width: 16px;
  height: 16px;
  background: var(--md-outline);
  border-radius: var(--md-shape-switch-handle);   /* 8px — BRAND, soft square */
  transition:
    transform var(--flow-sweep-duration-ui) var(--flow-sweep-ease),
    background var(--flow-sweep-duration-ui) var(--flow-sweep-ease),
    width var(--flow-sweep-duration-ui) var(--flow-sweep-ease),
    height var(--flow-sweep-duration-ui) var(--flow-sweep-ease);
}
.flow-toggle[aria-checked="true"] {
  border-color: var(--md-primary);
}
.flow-toggle[aria-checked="true"]::before {
  transform: translateX(0);
}
.flow-toggle[aria-checked="true"] .flow-toggle__thumb {
  transform: translateX(20px);
  background: var(--md-on-primary);
  width: 24px;
  height: 24px;
}

/* -- Flow Stagger / Page Enter ------------------------------------------ */
/* Użycie:
     <div class="flow-stagger">
       <div class="flow-stagger__item" style="--n: 0"> ... </div>
       <div class="flow-stagger__item" style="--n: 1"> ... </div>
     </div>

   Dodaj .flow-stagger--animate (np. po mount w JS) — odpala sekwencję.
   CSS custom prop --n ustawia pozycję w stagger (0, 1, 2, ...).
   Używa CSS animations (nie transition) żeby replay był deterministyczny
   — remove/re-add klasy zawsze re-triggeruje animację od zera. */
.flow-stagger__item {
  position: relative;
  opacity: 0;
  transform: translateY(12px);
}
.flow-stagger--animate .flow-stagger__item {
  animation: flow-stagger-enter var(--flow-sweep-duration-page) var(--flow-sweep-ease) both;
  animation-delay: calc(var(--n, 0) * var(--flow-sweep-stagger-page));
}
/* Replay reset: snap back to initial state without animation.
   JS clears data-replay after 1 frame, then adds --animate for fresh run. */
.flow-stagger[data-replay="reset"] .flow-stagger__item {
  animation: none !important;
  opacity: 0;
  transform: translateY(12px);
}
@keyframes flow-stagger-enter {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* --------------------------------------------------------------------------
   OVERLAY — Modal / Dialog / Sheet enter
   --------------------------------------------------------------------------
   Trzy warianty odsłaniania, wszystkie z jedną zasadą: scrim wjeżdża fade'em,
   content wjeżdża z kierunku (bottom sheet = z dołu, side panel = z prawej,
   dialog = scale + fade).

   Użycie:
     <div class="flow-overlay flow-overlay--open">
       <div class="flow-overlay__scrim"></div>
       <div class="flow-overlay__content flow-overlay--dialog"> ... </div>
     </div>

   Klasa --open odpala animację. Usunięcie = exit (accelerate easing).
   -------------------------------------------------------------------------- */
.flow-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 100;
}
.flow-overlay--open { pointer-events: auto; }

.flow-overlay__scrim {
  position: absolute;
  inset: 0;
  background: color-mix(in oklch, var(--md-scrim, #000) 50%, transparent);
  opacity: 0;
  transition: opacity var(--flow-duration-overlay) var(--flow-ease);
}
.flow-overlay--open .flow-overlay__scrim { opacity: 1; }

.flow-overlay__content {
  position: absolute;
  background: var(--md-surface-container-high);
  color: var(--md-on-surface);
  box-shadow: var(--md-elevation-3);
  /* Initial state — overridden per variant below */
  opacity: 0;
  transition:
    opacity var(--flow-duration-overlay) var(--flow-ease),
    transform var(--flow-duration-overlay) var(--flow-ease);
}
.flow-overlay--open .flow-overlay__content { opacity: 1; transform: none; }

/* Dialog — centered, scale + fade */
.flow-overlay__content.flow-overlay--dialog {
  top: 50%; left: 50%;
  width: min(440px, calc(100% - 48px));
  transform: translate(-50%, -50%) scale(0.94);
  border-radius: var(--md-shape-card);
  padding: 24px;
}
.flow-overlay--open .flow-overlay__content.flow-overlay--dialog {
  transform: translate(-50%, -50%) scale(1);
}

/* Bottom sheet — slides up from bottom */
.flow-overlay__content.flow-overlay--sheet {
  left: 0; right: 0; bottom: 0;
  transform: translateY(100%);
  border-radius: var(--md-shape-card) var(--md-shape-card) 0 0;
  padding: 24px;
  max-height: 75%;
}

/* Side panel — slides in from right */
.flow-overlay__content.flow-overlay--panel {
  top: 0; right: 0; bottom: 0;
  width: min(380px, 85%);
  transform: translateX(100%);
  padding: 24px;
}

/* Exit easing — accelerate when closing */
.flow-overlay:not(.flow-overlay--open) .flow-overlay__content {
  transition-timing-function: var(--flow-ease-accelerate);
}
.flow-overlay:not(.flow-overlay--open) .flow-overlay__scrim {
  transition-timing-function: var(--flow-ease-accelerate);
}


/* --------------------------------------------------------------------------
   NOTIFICATION CARDS — kolorowe, stackujące się, prawa-góra
   --------------------------------------------------------------------------
   Cięższe komunikaty: info, success, error, warning. Z tytułem, opisem,
   ikoną i zamknięciem. Wjeżdżają z prawej, stack z góry (najnowszy nad).
   -------------------------------------------------------------------------- */
.flow-notification-host {
  position: absolute;
  top: 0; right: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  padding: 16px;
  pointer-events: none;
  z-index: 95;
  max-width: 380px;
  width: 100%;
}
.flow-notification {
  pointer-events: auto;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: 14px;
  padding: 14px 16px;
  width: 100%;
  border-radius: var(--md-shape-card);
  box-shadow: var(--md-elevation-3);
  opacity: 0;
  transform: translateX(120%);
  transition:
    opacity var(--flow-duration-toast) var(--flow-ease),
    transform var(--flow-duration-toast) var(--flow-ease);
}
.flow-notification--in { opacity: 1; transform: translateX(0); }
.flow-notification--out {
  opacity: 0;
  transform: translateX(120%);
  transition-timing-function: var(--flow-ease-accelerate);
}
.flow-notification__icon {
  font-size: 22px;
  line-height: 1;
  margin-top: 2px;
}
.flow-notification__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.flow-notification__title {
  font: var(--md-title-small);
  font-weight: 700;
}
.flow-notification__text {
  font: var(--md-body-small);
  opacity: 0.92;
}
.flow-notification__close {
  background: transparent;
  border: 0;
  padding: 6px;
  margin: -4px -4px 0 0;
  cursor: pointer;
  color: inherit;
  opacity: 0.7;
  border-radius: 999px;
  align-self: start;
  font-size: 14px;
  transition: opacity 140ms var(--flow-ease), background 140ms var(--flow-ease);
}
.flow-notification__close:hover {
  opacity: 1;
  background: color-mix(in oklch, currentColor 15%, transparent);
}

/* Variants */
.flow-notification--info    { background: var(--md-primary);            color: var(--md-on-primary); }
.flow-notification--success { background: var(--md-primary-container);  color: var(--md-on-primary-container); }
.flow-notification--error   { background: var(--md-error);              color: var(--md-on-error); }
.flow-notification--warning { background: #f59e0b;                      color: #1f1500; }


/* --------------------------------------------------------------------------
   TOAST / Snackbar
   -------------------------------------------------------------------------- */
.flow-toast-host {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 16px;
  pointer-events: none;
  z-index: 90;
}
.flow-toast {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 14px 20px;
  min-width: 260px;
  max-width: 480px;
  background: var(--md-inverse-surface);
  color: var(--md-inverse-on-surface);
  border-radius: var(--md-shape-card);
  font: var(--md-body-medium);
  box-shadow: var(--md-elevation-3);
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity var(--flow-duration-toast) var(--flow-ease),
    transform var(--flow-duration-toast) var(--flow-ease);
}
.flow-toast--in { opacity: 1; transform: translateY(0); }
.flow-toast--out {
  opacity: 0;
  transform: translateY(16px);
  transition-timing-function: var(--flow-ease-accelerate);
}
.flow-toast__icon { color: var(--md-inverse-primary); font-size: 18px; }
.flow-toast__action {
  margin-left: auto;
  background: transparent;
  border: 0;
  color: var(--md-inverse-primary);
  font: inherit;
  font-weight: 600;
  padding: 4px 8px;
  border-radius: var(--md-shape-button);
  cursor: pointer;
}


/* --------------------------------------------------------------------------
   ACCORDION / Disclosure
   --------------------------------------------------------------------------
   Height transition via grid-template-rows 0fr → 1fr (modern CSS trick —
   eliminuje potrzebę mierzenia wysokości w JS, działa z dynamicznym content).

   Użycie:
     <div class="flow-accordion-item" data-open>
       <button class="flow-accordion__trigger"> ... </button>
       <div class="flow-accordion__panel">
         <div class="flow-accordion__inner"> ... content ... </div>
       </div>
     </div>
   -------------------------------------------------------------------------- */
.flow-accordion-item {
  border-bottom: 1px solid var(--md-outline-variant);
}
.flow-accordion__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 4px;
  background: transparent;
  border: 0;
  text-align: left;
  cursor: pointer;
  color: var(--md-on-surface);
  font: var(--md-title-medium);
}
.flow-accordion__chevron {
  transition: transform var(--flow-duration-expand) var(--flow-ease);
  color: var(--md-on-surface-variant);
}
.flow-accordion-item[data-open] .flow-accordion__chevron {
  transform: rotate(180deg);
}
.flow-accordion__panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--flow-duration-expand) var(--flow-ease);
}
.flow-accordion-item[data-open] .flow-accordion__panel {
  grid-template-rows: 1fr;
}
.flow-accordion__inner {
  overflow: hidden;
  min-height: 0;
}
.flow-accordion__inner > * { padding-bottom: 20px; }


/* --------------------------------------------------------------------------
   FOCUS RING — brand-consistent keyboard focus
   --------------------------------------------------------------------------
   Pigułka primary-color oplata focusowany element. Używa :focus-visible,
   więc nie wyświetla się przy kliknięciu myszą. Animuje scale-in z lekkim
   overshoot żeby "wskakiwał" z energią.
   -------------------------------------------------------------------------- */
.flow-focus:focus-visible,
button.flow-focus:focus-visible,
a.flow-focus:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 1.5px var(--md-surface),
    0 0 0 3px var(--md-primary);
  animation: flow-focus-pop var(--flow-duration-micro) var(--flow-ease-spring);
}
@keyframes flow-focus-pop {
  0%   { box-shadow: 0 0 0 1.5px var(--md-surface), 0 0 0 0 var(--md-primary); }
  100% { box-shadow: 0 0 0 1.5px var(--md-surface), 0 0 0 3px var(--md-primary); }
}


/* --------------------------------------------------------------------------
   SKELETON — loading shimmer
   --------------------------------------------------------------------------
   Kierunkowy shimmer (od lewej), spójny z sweep. Nie pulsuje — jedzie.
   Użycie: <div class="flow-skeleton" style="width: 200px; height: 16px"></div>
   -------------------------------------------------------------------------- */
.flow-skeleton {
  position: relative;
  overflow: hidden;
  background: var(--md-surface-container-high);
  border-radius: var(--md-shape-button);
}
.flow-skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    transparent 0%,
    color-mix(in oklch, var(--md-on-surface) 8%, transparent) 50%,
    transparent 100%
  );
  transform: translateX(-100%);
  animation: flow-skeleton-shimmer var(--flow-duration-shimmer) linear infinite;
}
@keyframes flow-skeleton-shimmer {
  to { transform: translateX(100%); }
}


/* --------------------------------------------------------------------------
   CHART ENTER — bars rise, lines draw
   --------------------------------------------------------------------------
   .flow-bar wjeżdża od dołu ze skalą Y, z stagger według --n.
   .flow-line rysuje się od 0 do pełnej długości (stroke-dashoffset trick).
   -------------------------------------------------------------------------- */
.flow-bar {
  transform: scaleY(0);
  transform-origin: bottom center;
  opacity: 0;
}
.flow-bars--animate .flow-bar {
  animation: flow-bar-rise var(--flow-duration-data) var(--flow-ease) both;
  animation-delay: calc(var(--n, 0) * var(--flow-sweep-stagger-page));
}
@keyframes flow-bar-rise {
  from { transform: scaleY(0); opacity: 0; }
  to   { transform: scaleY(1); opacity: 1; }
}

.flow-line {
  stroke-dasharray: var(--flow-line-length, 1000);
  stroke-dashoffset: var(--flow-line-length, 1000);
}
.flow-line--animate {
  animation: flow-line-draw var(--flow-duration-data) var(--flow-ease) both;
}
@keyframes flow-line-draw {
  to { stroke-dashoffset: 0; }
}


/* --------------------------------------------------------------------------
   SUCCESS CHECKMARK — draw animation
   --------------------------------------------------------------------------
   <svg class="flow-check flow-check--animate"> … </svg>
   Circle scales in (spring), then check strokes on.
   -------------------------------------------------------------------------- */
.flow-check__circle {
  transform-origin: center;
  transform: scale(0);
}
.flow-check__tick {
  stroke-dasharray: 30;
  stroke-dashoffset: 30;
}
.flow-check--animate .flow-check__circle {
  animation: flow-check-circle var(--flow-duration-draw) var(--flow-ease-spring) both;
}
.flow-check--animate .flow-check__tick {
  animation: flow-check-tick var(--flow-duration-draw) var(--flow-ease) both;
  animation-delay: 180ms;
}
@keyframes flow-check-circle {
  from { transform: scale(0); }
  to   { transform: scale(1); }
}
@keyframes flow-check-tick {
  to { stroke-dashoffset: 0; }
}


/* --------------------------------------------------------------------------
   ERROR SHAKE — negative feedback
   --------------------------------------------------------------------------
   Dla invalid form submits, błędnego PIN itp. Krótki, czytelny. */
.flow-shake--animate {
  animation: flow-shake var(--flow-duration-shake) cubic-bezier(.36,.07,.19,.97);
}
@keyframes flow-shake {
  10%, 90% { transform: translateX(-2px); }
  20%, 80% { transform: translateX(4px); }
  30%, 50%, 70% { transform: translateX(-8px); }
  40%, 60% { transform: translateX(8px); }
}


/* --------------------------------------------------------------------------
   FAB — scale-in + tap response
   --------------------------------------------------------------------------
   Mount with overshoot; tap = quick depress. */
.flow-fab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border: 0;
  border-radius: var(--md-shape-card);
  background: var(--md-primary-container);
  color: var(--md-on-primary-container);
  font-size: 20px;
  box-shadow: var(--md-elevation-3);
  cursor: pointer;
  transition: transform var(--flow-duration-micro) var(--flow-ease);
}
.flow-fab--enter {
  animation: flow-fab-in var(--flow-duration-overlay) var(--flow-ease-spring) both;
}
.flow-fab:active { transform: scale(0.92); }
@keyframes flow-fab-in {
  from { transform: scale(0); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}


/* --------------------------------------------------------------------------
   PAGE TRANSITION — view transitions (fallback to CSS)
   --------------------------------------------------------------------------
   Container-level: fresh view wjeżdża od prawej, stary wyjeżdża w lewo.
   Żeby było działające bez @view-transition API, używamy klas --in/--out. */
.flow-page {
  position: relative;
  overflow: hidden;
}
.flow-page__view {
  position: absolute;
  inset: 0;
  transform: translateX(0);
  opacity: 1;
  transition:
    transform var(--flow-duration-overlay) var(--flow-ease),
    opacity var(--flow-duration-overlay) var(--flow-ease);
}
.flow-page__view--enter-from-right { transform: translateX(100%); opacity: 0; }
.flow-page__view--enter-from-left  { transform: translateX(-100%); opacity: 0; }
.flow-page__view--exit-to-left     { transform: translateX(-30%); opacity: 0; }
.flow-page__view--exit-to-right    { transform: translateX(30%); opacity: 0; }


/* --------------------------------------------------------------------------
   TAP RIPPLE — Flow-variant (kierunkowy, od punktu kliknięcia)
   --------------------------------------------------------------------------
   JS ustawia --ripple-x/y z clientX/Y relative to element, potem dodaje
   klasę --ripple dla jednego animation run. */
.flow-ripple {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.flow-ripple::after {
  content: "";
  position: absolute;
  left: var(--ripple-x, 50%);
  top: var(--ripple-y, 50%);
  width: 0; height: 0;
  border-radius: 50%;
  background: color-mix(in oklch, var(--md-primary) 25%, transparent);
  transform: translate(-50%, -50%);
  opacity: 0;
  pointer-events: none;
  z-index: 1;
}
.flow-ripple--ripple::after {
  animation: flow-ripple-expand 550ms var(--flow-ease) both;
}
@keyframes flow-ripple-expand {
  0%   { width: 0; height: 0; opacity: 0.6; }
  100% { width: 500px; height: 500px; opacity: 0; }
}


/* --------------------------------------------------------------------------
   REDUCED MOTION — wszystkie sweep/animacje redukują się
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .md-button--sweep,
  .btn-filled.sweep,
  .md-button--sweep::after,
  .btn-filled.sweep::after,
  .flow-tab,
  .flow-tab::before,
  .flow-tabs__indicator,
  .flow-nav-item,
  .flow-nav-item::before,
  .flow-nav-rail__indicator,
  .flow-list-item,
  .flow-list-item::before,
  .flow-toggle,
  .flow-toggle::before,
  .flow-toggle__thumb,
  .flow-stagger__item,
  .flow-overlay__scrim,
  .flow-overlay__content,
  .flow-toast,
  .flow-accordion__panel,
  .flow-accordion__chevron,
  .flow-bar,
  .flow-line,
  .flow-check__circle,
  .flow-check__tick,
  .flow-fab,
  .flow-page__view,
  .flow-skeleton::after,
  .flow-ripple::after {
    transition-duration: 1ms !important;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
  }
}
