/* =========================================================================
   EventPix — biblioteka komponentów
   Kierunek C. Wymaga design-tokens.css wczytanego wcześniej.

   ZASADA: ani jednej zahardkodowanej wartości koloru czy rozmiaru. Wszystko
   przez var(). Dopuszczone są wyłącznie słowa kluczowe (transparent,
   currentColor, inherit), zera, ułamki układu (1fr) i procenty.

   Konwencja nazw za gallery.css: blok__element--modyfikator, bez prefiksu.

   Stany: każdy komponent interaktywny obsługuje default / hover / focus /
   active / disabled / error. Selektory pisane jako :is(:hover, .is-hover),
   żeby katalog mógł pokazać stan bez wywoływania go myszą.
   ========================================================================= */

:root {
  /* Stałe komponentowe — rzeczy, których nie ma w zestawie tokenów, bo są
     proporcjami układu, nie skalą projektową. */
  --media-ratio: 4 / 3;
  --control-height: var(--size-touch);
  /* 44px, nie 40: --space-10 dawało cel poniżej minimum dotykowego, a filtry
     i przycisk menu są obsługiwane palcem. Złożone z tokenów, bo warstwa
     komponentów nie wpisuje wartości wprost. */
  --control-height-sm: calc(var(--space-10) + var(--space-1));
  --control-height-lg: var(--space-12);
  --spinner-size: var(--space-5);
  --spinner-width: var(--border-2);
  --skeleton-sheen: var(--duration-deliberate);

  /* Lightbox i plakietka na zdjęciu muszą być ciemne w OBU motywach — zdjęcie
     jest treścią i ma leżeć na ciemnym tle niezależnie od preferencji widza.
     Dlatego biorą kolor ze SKALI (--neutral-900/0), która nie przełącza się
     między motywami, a nie z aliasu --color-text, który by się odwrócił i dał
     w ciemnym motywie prawie biały lightbox. */
  --action-bar-height: calc(var(--control-height) + var(--space-6));

  --scrim: var(--neutral-900);
  --scrim-ink: var(--neutral-0);
}

/* =========================================================================
   PRZYCISK
   Wystąpienia: „Wyślij zdjęcia", „Utwórz wydarzenie", „Zamknij wydarzenie",
   „Generuj link i QR", „Usuń wydarzenie i media", „Zaloguj".
   ========================================================================= */

.button {
  display: inline-flex;
  gap: var(--space-2);
  align-items: center;
  justify-content: center;
  min-height: var(--control-height);
  padding-inline: var(--space-5);
  border: var(--border-1) solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-snug);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--ease-standard),
    border-color var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard);
}

.button:focus-visible,
.button.is-focus {
  outline: var(--focus-width) solid var(--color-focus);
  outline-offset: var(--focus-offset);
}

/* --- warianty --- */
.button--primary {
  border-color: var(--color-brand);
  background: var(--color-brand);
  color: var(--color-text-on-brand);
}
.button--primary:is(:hover, .is-hover):not(:disabled, .is-disabled) {
  border-color: var(--color-brand-hover);
  background: var(--color-brand-hover);
}
.button--primary:is(:active, .is-active):not(:disabled, .is-disabled) {
  border-color: var(--color-brand-active);
  background: var(--color-brand-active);
}

.button--secondary {
  border-color: var(--color-border-interactive);
  background: var(--color-surface);
  color: var(--color-text);
}
.button--secondary:is(:hover, .is-hover):not(:disabled, .is-disabled) {
  border-color: var(--color-brand);
  background: var(--color-brand-subtle);
  color: var(--color-brand-active);
}
.button--secondary:is(:active, .is-active):not(:disabled, .is-disabled) {
  background: var(--color-brand-border);
}

.button--ghost { color: var(--color-brand); }
.button--ghost:is(:hover, .is-hover):not(:disabled, .is-disabled) {
  background: var(--color-brand-subtle);
}
.button--ghost:is(:active, .is-active):not(:disabled, .is-disabled) {
  background: var(--color-brand-border);
}

/* Usunięcie wydarzenia i mediów — jedyne nieodwracalne akcje w aplikacji. */
.button--danger {
  border-color: var(--color-error);
  background: var(--color-error);
  color: var(--color-text-on-brand);
}
.button--danger:is(:hover, .is-hover):not(:disabled, .is-disabled) {
  border-color: var(--color-error-strong);
  background: var(--color-error-strong);
}
.button--danger:is(:active, .is-active):not(:disabled, .is-disabled) {
  background: var(--color-error-strong);
}

/* --- rozmiary --- */
.button--sm {
  min-height: var(--control-height-sm);
  padding-inline: var(--space-3);
  font-size: var(--text-sm);
}
.button--lg {
  min-height: var(--control-height-lg);
  padding-inline: var(--space-6);
  font-size: var(--text-lg);
}

/* --- stany --- */
.button:is(:disabled, .is-disabled) {
  opacity: 0.5;
  cursor: not-allowed;
}

.button--loading { position: relative; color: transparent; }
.button--loading .button__label { visibility: hidden; }
.button--loading::after {
  position: absolute;
  width: var(--spinner-size);
  height: var(--spinner-size);
  border: var(--spinner-width) solid currentColor;
  border-block-start-color: transparent;
  border-radius: var(--radius-full);
  color: var(--color-text-on-brand);
  animation: spin var(--duration-deliberate) var(--ease-linear) infinite;
  content: "";
}
.button--loading.button--secondary::after,
.button--loading.button--ghost::after { color: var(--color-brand); }

.button--block { display: flex; width: 100%; }

/* =========================================================================
   POLE FORMULARZA
   Wystąpienia: imię gościa, hasło wydarzenia, nazwa wydarzenia, kolor ikony
   (pattern #rrggbb), personalizacja JSON (textarea), potwierdzenie slugu.
   ========================================================================= */

.field { display: grid; gap: var(--space-2); }

.field__label {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--color-text);
}

.field__required { color: var(--color-error); }

.input,
.textarea,
.select {
  width: 100%;
  min-height: var(--control-height);
  padding: var(--space-2) var(--space-3);
  border: var(--border-1) solid var(--color-border-interactive);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  transition:
    border-color var(--duration-fast) var(--ease-standard),
    background-color var(--duration-fast) var(--ease-standard);
}

.textarea {
  min-height: calc(var(--space-24) + var(--space-4));
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  resize: vertical;
}

.input::placeholder,
.textarea::placeholder { color: var(--color-text-subtle); }

.input:is(:hover, .is-hover):not(:disabled, .is-disabled),
.textarea:is(:hover, .is-hover):not(:disabled, .is-disabled),
.select:is(:hover, .is-hover):not(:disabled, .is-disabled) {
  border-color: var(--color-brand);
}

.input:is(:focus-visible, .is-focus),
.textarea:is(:focus-visible, .is-focus),
.select:is(:focus-visible, .is-focus) {
  border-color: var(--color-brand);
  outline: var(--focus-width) solid var(--color-focus);
  outline-offset: var(--focus-offset);
}

.input:is(:disabled, .is-disabled),
.textarea:is(:disabled, .is-disabled),
.select:is(:disabled, .is-disabled) {
  background: var(--color-surface-sunken);
  color: var(--color-text-subtle);
  cursor: not-allowed;
}

/* Błąd sygnalizowany kolorem ORAZ tekstem pod polem — sam kolor nie wystarczy. */
.field--invalid .input,
.field--invalid .textarea,
.field--invalid .select,
.input[aria-invalid="true"],
.textarea[aria-invalid="true"],
.select[aria-invalid="true"] {
  border-color: var(--color-error);
}
.field--invalid .input:is(:focus-visible, .is-focus),
.field--invalid .select:is(:focus-visible, .is-focus) {
  outline-color: var(--color-error);
}

.field__hint {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.field__error {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-error-strong);
}
.field__error::before {
  flex: none;
  width: var(--space-2);
  height: var(--space-2);
  margin-block-start: calc(var(--space-2) - var(--space-px));
  border-radius: var(--radius-full);
  background: var(--color-error);
  content: "";
}

/* --- select --- */
.select {
  padding-inline-end: var(--space-8);
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position:
    right var(--space-4) top 50%,
    right calc(var(--space-4) - var(--space-1)) top 50%;
  background-size: var(--space-1) var(--space-1), var(--space-1) var(--space-1);
  background-repeat: no-repeat;
}

/* --- plik: input[type=file] nie da się ostylować, więc opakowanie --- */
.file {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-3);
  border: var(--border-2) dashed var(--color-border-interactive);
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}
.file:is(:hover, .is-hover) { border-color: var(--color-brand); }
.file:focus-within {
  border-color: var(--color-brand);
  outline: var(--focus-width) solid var(--color-focus);
  outline-offset: var(--focus-offset);
}

/* =========================================================================
   CHECKBOX I RADIO
   Wystąpienia: filtry galerii, przyszłe przełączniki panelu.
   ========================================================================= */

.choice {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  min-height: var(--control-height);
  cursor: pointer;
}

.choice__input {
  flex: none;
  width: var(--space-5);
  height: var(--space-5);
  margin: 0;
  accent-color: var(--color-brand);
  cursor: inherit;
}
.choice__input:is(:focus-visible, .is-focus) {
  outline: var(--focus-width) solid var(--color-focus);
  outline-offset: var(--focus-offset);
}
.choice__input:disabled { cursor: not-allowed; }

.choice__label { font-size: var(--text-base); color: var(--color-text); }
.choice:has(.choice__input:disabled) { cursor: not-allowed; opacity: 0.5; }
.choice--invalid .choice__label { color: var(--color-error-strong); }

/* =========================================================================
   KARTA
   Wystąpienia: sekcje dashboardu, panele ekranu gościa, karta galerii.
   ========================================================================= */

.card {
  display: flex;
  flex-direction: column;
  border: var(--border-1) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-1);
}

/* W ciemnym motywie cień nie niesie uniesienia, więc niosą je powierzchnie.
   Jeden blok dla wszystkich elementów uniesionych, żeby nie rozjechały się
   między sobą. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) :is(.card, .media-card, .action-bar, .dialog) {
    background: var(--color-surface-raised);
  }
}
:root[data-theme="dark"] :is(.card, .media-card, .action-bar, .dialog) {
  background: var(--color-surface-raised);
}

.card__header {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: baseline;
  justify-content: space-between;
  padding: var(--space-5) var(--space-5) 0;
}

.card__title {
  margin: 0;
  font-family: var(--font-heading);
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-h3);
}

.card__body { padding: var(--space-5); }
.card__footer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: 0 var(--space-5) var(--space-5);
}

/* --- karta medium: zastępuje .gallery-card --- */
.media-card {
  overflow: hidden;
  border: var(--border-1) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-1);
}

.media-card__media {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  padding: 0;
  border: 0;
  aspect-ratio: var(--media-ratio);
  overflow: hidden;
  background: var(--color-surface-sunken);
  color: var(--color-text-muted);
  cursor: pointer;
}
.media-card__media img { width: 100%; height: 100%; object-fit: cover; }
.media-card__media:is(:focus-visible, .is-focus) {
  outline: var(--focus-width) solid var(--color-focus);
  outline-offset: calc(var(--focus-width) * -1);
}
.media-card__badge {
  position: absolute;
  inset-block-start: var(--space-3);
  inset-inline-end: var(--space-3);
}
.media-card__author {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  overflow-wrap: anywhere;
}

/* =========================================================================
   BADGE / STATUS
   Desygnaty: media.status, download_jobs.status, events.status, blokady.
   ========================================================================= */

.badge {
  display: inline-flex;
  gap: var(--space-2);
  align-items: center;
  padding: var(--space-1) var(--space-3);
  border: var(--border-1) solid var(--color-border);
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
  color: var(--color-text-muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-snug);
  white-space: nowrap;
}

/* Kropka niesie ten sam sygnał co kolor tła — status czytelny także wtedy,
   gdy kolor jest nieodróżnialny. */
.badge::before {
  flex: none;
  width: var(--space-2);
  height: var(--space-2);
  border-radius: var(--radius-full);
  background: currentColor;
  content: "";
}
.badge--nodot::before { display: none; }

.badge--success {
  border-color: var(--color-success-border);
  background: var(--color-success-subtle);
  color: var(--color-success-strong);
}
.badge--info {
  border-color: var(--color-info-border);
  background: var(--color-info-subtle);
  color: var(--color-info-strong);
}
.badge--warning {
  border-color: var(--color-warning-border);
  background: var(--color-warning-subtle);
  color: var(--color-warning-strong);
}
.badge--error {
  border-color: var(--color-error-border);
  background: var(--color-error-subtle);
  color: var(--color-error-strong);
}

/* Wariant na zdjęciu — musi być czytelny na dowolnej fotografii. */
.badge--overlay {
  border-color: transparent;
  background: var(--scrim);
  color: var(--scrim-ink);
}

/* =========================================================================
   MONOGRAM — kafelek ikony wydarzenia

   W APLIKACJI ten komponent jest nakładany na <img src="/events/:slug/icon">.
   Kolor tła NIE pochodzi z CSS, tylko jest zapieczony w SVG zwracanym przez
   ten endpoint — CSP (style-src 'self') zabrania atrybutu style, więc nie ma
   jak podać koloru per wydarzenie z arkusza. Endpoint oddaje albo wgrany
   obrazek, albo wygenerowany monogram, a widok nie musi wiedzieć który.

   Klasa obsługuje dwa przypadki:
     img.monogram   — wariant aplikacji; tło działa jako placeholder do czasu
                      wczytania obrazka
     span.monogram  — wariant czysto CSS-owy, gdy literę i kolor da się podać
                      ze stylów (katalog, przyszłe widoki bez per-event koloru)
   ========================================================================= */

.monogram {
  display: grid;
  flex: none;
  place-items: center;
  width: var(--monogram-size, var(--space-10));
  height: var(--monogram-size, var(--space-10));
  border-radius: var(--radius-md);
  background: var(--monogram-color, var(--color-brand));
  color: var(--color-text-on-brand);
  font-family: var(--font-heading);
  /* Litera skalowana od kafelka, nie wpisana na sztywno — inaczej monogram
     dekoracyjny bywa większy niż nagłówek, przy którym stoi. */
  font-size: calc(var(--monogram-size, var(--space-10)) * 0.44);
  font-weight: var(--weight-bold);
  line-height: 1;
}

/* Wariant obrazkowy: kadrowanie i przycięcie do kształtu kafelka. */
img.monogram,
.monogram > img {
  object-fit: cover;
  overflow: hidden;
}

.monogram--lg { --monogram-size: var(--space-16); }
.monogram--sm { --monogram-size: var(--space-6); }

/* Na wąskim ekranie duży wariant schodzi o stopień — 64px kafelka zjadało
   cały pasek tożsamości na telefonie. */
@media (max-width: 40rem) {
  .monogram--lg { --monogram-size: var(--space-12); }
}

/* =========================================================================
   MODAL — lightbox galerii
   ========================================================================= */

.modal {
  position: fixed;
  inset: 0;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: var(--space-4);
  padding: var(--space-4);
  background: var(--scrim);
  color: var(--scrim-ink);
}
.modal[hidden] { display: none; }

.modal__bar {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  justify-content: space-between;
}

.modal__title {
  margin: 0;
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
}

.modal__stage {
  display: grid;
  place-items: center;
  min-height: 0;
}
.modal__stage :is(img, video) {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.modal__footer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  justify-content: space-between;
}

/* Przyciski modala żyją na zdjęciu, nie na powierzchni aplikacji. */
.modal .button {
  border-color: var(--scrim-ink);
  background: transparent;
  color: var(--scrim-ink);
}
.modal .button:is(:hover, .is-hover):not(:disabled, .is-disabled) {
  background: var(--scrim-ink);
  color: var(--scrim);
}
.modal .button:is(:focus-visible, .is-focus) { outline-color: var(--scrim-ink); }

/* --- dialog na powierzchni: potwierdzenia --- */
.dialog {
  width: min(100%, var(--space-24) * 5);
  padding: var(--space-6);
  border: var(--border-1) solid var(--color-border);
  border-radius: var(--radius-xl);
  background: var(--color-surface);
  box-shadow: var(--shadow-4);
}
.dialog__title {
  margin: 0 0 var(--space-3);
  font-family: var(--font-heading);
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
}
.dialog__body { margin: 0 0 var(--space-5); color: var(--color-text-muted); }
.dialog__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); justify-content: flex-end; }

/* =========================================================================
   NAWIGACJA
   Wystąpienia: pasek panelu, selektor języka, stopka z linkiem admina.
   ========================================================================= */

.nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  align-items: center;
}

.nav__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--control-height-sm);
  padding-inline: var(--space-3);
  border-radius: var(--radius-md);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  text-decoration: none;
  transition: background-color var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.nav__link:is(:hover, .is-hover) {
  background: var(--color-surface-sunken);
  color: var(--color-text);
}
.nav__link:is(:focus-visible, .is-focus) {
  outline: var(--focus-width) solid var(--color-focus);
  outline-offset: var(--focus-offset);
}
.nav__link:is(:active, .is-active) { background: var(--color-brand-border); }
.nav__link[aria-current] {
  background: var(--color-brand-subtle);
  color: var(--color-brand-active);
  font-weight: var(--weight-semibold);
}

/* --- paginacja moderacji (24 na stronę) --- */
.pager {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  justify-content: space-between;
}
.pager__status {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

/* =========================================================================
   LISTA POZYCJI I TABELA
   Wystąpienia: lista wydarzeń, moderacja, wykryte adresy, media wg gości.
   ========================================================================= */

.item-list {
  display: grid;
  gap: var(--space-2);
  grid-template-columns: minmax(0, 1fr);
  margin: 0;
  padding: 0;
  list-style: none;
}

.item {
  display: flex;
  gap: var(--space-4);
  align-items: center;
  padding: var(--space-3) var(--space-4);
  border: var(--border-1) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--duration-fast) var(--ease-standard),
              background-color var(--duration-fast) var(--ease-standard);
}
.item:is(:hover, .is-hover) {
  border-color: var(--color-brand);
  background: var(--color-brand-subtle);
}
.item:is(:focus-visible, .is-focus) {
  outline: var(--focus-width) solid var(--color-focus);
  outline-offset: var(--focus-offset);
}
.item:is(:active, .is-active) { background: var(--color-brand-border); }
.item.is-disabled { opacity: 0.5; pointer-events: none; }

.item__body { flex: 1 1 auto; min-width: 0; }
.item__title {
  display: block;
  margin: 0;
  overflow: hidden;
  font-weight: var(--weight-semibold);
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Adres musi być czytelny w całości: wielokropek gubi różnicę między
   192.168.10.42 a 192.168.10.43, więc tu tekst łamie się zamiast znikać. */
.item__title--wrap {
  overflow: visible;
  white-space: normal;
  overflow-wrap: anywhere;
}

.item__meta {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.item__actions { display: flex; flex: none; gap: var(--space-2); align-items: center; }

.table-scroll { overflow-x: auto; border: var(--border-1) solid var(--color-border); border-radius: var(--radius-lg); }

.table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.table :is(th, td) {
  padding: var(--space-3) var(--space-4);
  border-block-end: var(--border-1) solid var(--color-border);
  text-align: start;
}
.table thead th {
  background: var(--color-surface-sunken);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
  white-space: nowrap;
}
.table tbody tr:last-child :is(th, td) { border-block-end: 0; }
.table__num { font-variant-numeric: tabular-nums; text-align: end; }

/* --- lista definicji: przegląd wydarzenia i statystyki --- */
.data-list { display: grid; gap: var(--space-3); margin: 0; }
.data-list__row { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); justify-content: space-between; }
.data-list dt { font-size: var(--text-sm); color: var(--color-text-muted); }
.data-list dd {
  margin: 0;
  min-width: 0;
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
  /* Ścieżka katalogu danych i adres instancji potrafią być dłuższe niż wiersz,
     a na telefonie rozpychały układ w bok. */
  overflow-wrap: anywhere;
}
.data-list code { font-family: var(--font-mono); font-size: var(--text-sm); font-weight: var(--weight-regular); }

/* =========================================================================
   ALERT I TOAST
   Wystąpienia: złe hasło, wydarzenie zamknięte, archiwum gotowe, błąd galerii.
   ========================================================================= */

.alert {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  padding: var(--space-4);
  border: var(--border-1) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface-sunken);
  color: var(--color-text);
}
.alert__dot {
  flex: none;
  width: var(--space-3);
  height: var(--space-3);
  margin-block-start: calc(var(--space-1) + var(--space-px));
  border-radius: var(--radius-full);
  background: currentColor;
}
.alert__body { flex: 1 1 auto; min-width: 0; }
.alert__title { margin: 0; font-weight: var(--weight-semibold); }
.alert__text { margin: 0; font-size: var(--text-sm); opacity: 0.85; }

.alert--success { border-color: var(--color-success-border); background: var(--color-success-subtle); color: var(--color-success-strong); }
.alert--info    { border-color: var(--color-info-border);    background: var(--color-info-subtle);    color: var(--color-info-strong); }
.alert--warning { border-color: var(--color-warning-border); background: var(--color-warning-subtle); color: var(--color-warning-strong); }
.alert--error   { border-color: var(--color-error-border);   background: var(--color-error-subtle);   color: var(--color-error-strong); }

.toast-region {
  position: fixed;
  inset-block-end: var(--space-4);
  inset-inline: var(--space-4);
  display: grid;
  gap: var(--space-2);
  justify-items: center;
  pointer-events: none;
}
.toast {
  width: min(100%, var(--space-24) * 4);
  box-shadow: var(--shadow-3);
  pointer-events: auto;
}

/* =========================================================================
   SPINNER, SKELETON, PROGRESS
   ========================================================================= */

.spinner {
  display: inline-block;
  width: var(--spinner-size);
  height: var(--spinner-size);
  border: var(--spinner-width) solid var(--color-border-interactive);
  border-block-start-color: var(--color-brand);
  border-radius: var(--radius-full);
  animation: spin var(--duration-deliberate) var(--ease-linear) infinite;
}
.spinner--lg { --spinner-size: var(--space-10); --spinner-width: var(--border-3); }

@keyframes spin { to { transform: rotate(1turn); } }

.skeleton {
  /* display:block jest konieczny, nie kosmetyczny: skeleton bywa <span>em,
     a na elemencie inline wysokość nie działa i placeholder znika. */
  display: block;
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
  background-image: linear-gradient(
    90deg,
    transparent 0%,
    var(--color-border) 50%,
    transparent 100%
  );
  background-size: 200% 100%;
  animation: skeleton-sheen calc(var(--skeleton-sheen) * 2) var(--ease-linear) infinite;
}
.skeleton--text { height: var(--space-4); }
.skeleton--title { height: var(--space-6); }
.skeleton--thumb { aspect-ratio: var(--media-ratio); border-radius: var(--radius-lg); }

@keyframes skeleton-sheen {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}

/* Pasek postępu uploadu — element <progress> stylowany w obu silnikach. */
.progress {
  width: 100%;
  height: var(--space-2);
  appearance: none;
  border: 0;
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
  overflow: hidden;
}
.progress::-webkit-progress-bar { background: var(--color-surface-sunken); }
.progress::-webkit-progress-value {
  background: var(--color-brand);
  transition: inline-size var(--duration-normal) var(--ease-standard);
}
.progress::-moz-progress-bar { background: var(--color-brand); }

/* =========================================================================
   STAN PUSTY
   Wystąpienia: brak wydarzeń, pusta galeria, brak mediów do moderacji,
   brak aktywności gości.
   ========================================================================= */

.empty {
  display: grid;
  gap: var(--space-3);
  justify-items: center;
  padding: var(--space-12) var(--space-6);
  border: var(--border-2) dashed var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  text-align: center;
}
.empty__title {
  margin: 0;
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
}
.empty__text {
  max-width: var(--measure);
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* =========================================================================
   DISCLOSURE — menu gościa (details/summary)
   ========================================================================= */

.disclosure {
  border: var(--border-1) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}
.disclosure__summary {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  min-height: var(--control-height);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-lg);
  font-weight: var(--weight-semibold);
  cursor: pointer;
}
.disclosure__summary:is(:hover, .is-hover) { background: var(--color-surface-sunken); }
.disclosure__summary:is(:focus-visible, .is-focus) {
  outline: var(--focus-width) solid var(--color-focus);
  outline-offset: calc(var(--focus-offset) * -1);
}
.disclosure__content { display: grid; gap: var(--space-4); padding: 0 var(--space-4) var(--space-4); }

/* =========================================================================
   RUCH — wyłączenie na żądanie użytkownika
   ========================================================================= */

@media (prefers-reduced-motion: reduce) {
  .spinner,
  .skeleton,
  .button--loading::after { animation: none; }
  .skeleton { background-image: none; }
  * { transition-duration: var(--duration-instant) !important; }
}

/* =========================================================================
   PASEK AKCJI PRZYKLEJONY DO DOŁU
   Ujawnione przez makietę strefy gościa: na telefonie akcja priorytetowa
   musi być w zasięgu kciuka, a nie pod całą galerią.
   Obszar przewijania kompensuje jego wysokość przez --action-bar-height,
   inaczej pasek przykrywa ostatnie kafelki.
   ========================================================================= */

.action-bar {
  position: sticky;
  inset-block-end: 0;
  z-index: 2;
  padding: var(--space-3) var(--space-4);
  border-block-start: var(--border-1) solid var(--color-border);
  background: var(--color-surface);
  box-shadow: var(--shadow-3);
}

/* Powyżej progu dotykowego akcja wraca do paska górnego. */
@media (min-width: 40rem) { .action-bar--mobile-only { display: none; } }

/* =========================================================================
   FILTR SEGMENTOWY
   Trzy wykluczające się opcje. <select> jest tu za ciężki: wymaga otwarcia
   listy, żeby zobaczyć trzy słowa.
   ========================================================================= */

.segmented {
  display: inline-flex;
  padding: var(--space-1);
  border: var(--border-1) solid var(--color-border);
  border-radius: var(--radius-full);
  background: var(--color-surface);
}

.segmented__option {
  min-height: var(--control-height-sm);
  padding-inline: var(--space-4);
  border: 0;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text-muted);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}
.segmented__option:is(:hover, .is-hover):not([aria-pressed="true"], :disabled) {
  background: var(--color-brand-subtle);
  color: var(--color-brand-active);
}
.segmented__option:is(:focus-visible, .is-focus) {
  outline: var(--focus-width) solid var(--color-focus);
  outline-offset: var(--focus-offset);
}
.segmented__option[aria-pressed="true"] {
  background: var(--color-brand);
  color: var(--color-text-on-brand);
}
.segmented__option:disabled { opacity: 0.5; cursor: not-allowed; }

/* =========================================================================
   WIERSZ POSTĘPU POJEDYNCZEGO PLIKU
   Upload jest wielopikowy i częściowo udany — status musi być per plik,
   nie globalny.
   ========================================================================= */

.upload-row { display: grid; gap: var(--space-1); }

.upload-row__head {
  display: flex;
  gap: var(--space-3);
  align-items: baseline;
  justify-content: space-between;
  font-size: var(--text-sm);
}

.upload-row__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.upload-row__status {
  flex: none;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}
.upload-row--done .upload-row__status { color: var(--color-success-strong); }
.upload-row--error .upload-row__status { color: var(--color-error-strong); }

/* =========================================================================
   WYKRES AKTYWNOŚCI
   SVG renderowany serwerowo. Bez wypełnień elementy dziedziczyły czerń, a bez
   ograniczenia wysokości viewBox rozciągał się na szerokość karty i skalował
   słupki oraz liczby kilkunastokrotnie.
   ========================================================================= */

.activity-chart {
  width: 100%;
  height: var(--space-24);
  overflow: visible;
}

.activity-chart__bar { fill: var(--color-brand); }

.activity-chart__axis {
  stroke: var(--color-border);
  stroke-width: 1;
}

.activity-chart__value {
  fill: var(--color-text);
  font-family: var(--font-body);
  /* W SVG font-size jest w jednostkach użytkownika, więc token rem
     przelicza się na tę samą liczbę jednostek co piksele. */
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}

.activity-chart__day {
  fill: var(--color-text-muted);
  font-family: var(--font-body);
  font-size: var(--text-xs);
}

/* =========================================================================
   GRUPA W PANELU — dwa formularze o tych samych polach obok siebie
   Bez własnego nagłówka i ramki „login i hasło" powtórzone dwa razy były
   nie do rozróżnienia.
   ========================================================================= */

.panel-group {
  padding: var(--space-4);
  border: var(--border-1) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
}

.panel-group__title {
  margin: 0;
  font-family: var(--font-heading);
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
}

/* Rząd przycisków — dwa eksporty wydruku stoją obok siebie, bo dzielą jeden
   formularz i jeden wybór wzoru. */
.button-row { display: flex; flex-wrap: wrap; gap: var(--space-3); }

/* Próbnik koloru — natywny input type="color" jest niski i szeroki na całe
   pole, więc dostaje własną wysokość i pas na podgląd barwy. */
.input--color {
  height: var(--space-10);
  padding: var(--space-1);
  cursor: pointer;
}

/* Pole tekstowe z próbnikiem obok: kolor wpisuje się z klawiatury albo wybiera
   z palety, a przycisk czyści wartość, bo próbnik pustej nie zna. */
.field__row {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  flex-wrap: wrap;
}

.field__row > .input:first-child { flex: 1 1 var(--space-24); }
.field__row > .input--color { flex: 0 0 var(--space-16); }

/* Sam próbnik bez pola tekstowego obok nie ma po co ciągnąć się przez całą
   szerokość formularza. */
.field > .input--color { max-width: var(--space-24); }

/* Kolor dotyczy wyłącznie wzoru „Flat", więc przy innym wyborze pole znika.
   Przeglądarki bez :has() pokazują je zawsze — to samo, co przed zmianą. */
.print-form:has(#printStyle option:checked:not([value="flat"])) .field--flat-only {
  display: none;
}

/* Miniatury wzoru: plakat i winietka obok siebie, w tej samej skali co eksport,
   tylko mniejsze. Wybór wzoru daje się przez to porównać bez generowania pliku. */
.print-preview {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  align-items: flex-start;
}

.print-preview__item {
  margin: 0;
  display: grid;
  gap: var(--space-2);
  justify-items: center;
}

.print-preview__thumb {
  display: block;
  height: auto;
  max-width: 100%;
  border: var(--space-px) solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  box-shadow: var(--shadow-1);
  /* Wymiana miniatury po zmianie wzoru trwa tyle, ile rasteryzacja na serwerze —
     przygaszenie mówi, że obrazek jest nieaktualny, zamiast mrugać pustym polem. */
  transition: opacity var(--duration-fast) var(--ease-standard);
}

.print-preview__thumb--pending { opacity: 0.45; }

.print-preview__thumb--poster { width: calc(var(--space-24) * 2); }
.print-preview__thumb--place-card { width: calc(var(--space-24) * 3); }

.print-preview__caption {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
