/* ==========================================================================
   Losda - arkusz stylow
   Zwykly CSS, bez frameworka i bez kroku budowania. Mobile-first: reguly
   bazowe opisuja telefon, sekcje @media rozszerzaja uklad na wieksze ekrany.
   Panel administratora (/admin/*) ma wlasny, szerszy uklad z bocznym menu -
   patrz sekcja "Panel administratora" na koncu pliku.
   ========================================================================== */

:root {
  /* Paleta z zatwierdzonych makiet */
  --bg: #FBF6EF;
  --surface: #FFFFFF;
  --surface-2: #F3EAE0;
  --line: #EDE2D2;
  --ink: #2A2521;
  --ink-soft: #8A7A6B;
  --ink-faint: #8D7A68;

  --accent: #DE5E3C;
  --accent-dark: #B94A2D;
  --accent-ink: #FFFFFF;
  --accent-soft: #FBE6DD;

  /* Brand green - the logo wordmark, headings on the new-brand pages, and
     the header's icon-only nav. Distinct from --free (also green, but a
     functional "this item is free" tag color, not a brand/heading color). */
  --brand-green: #1F3A2C;
  --brand-green-soft: #E6ECE3;

  /* Sage/olive secondary button - alongside the orange --accent for
     secondary actions (e.g. "choose files" on the new-listing form). */
  --secondary: #7D8F6E;
  --secondary-dark: #667559;
  --secondary-ink: #FFFFFF;

  --free: #4C7A5C;
  --free-bg: #E4EFE4;
  --promo: #C88A2E;
  --promo-bg: #FBF0D9;
  --warn: #C1483A;
  --warn-bg: #F7E4E1;

  --radius-lg: 20px;
  --radius-md: 14px;
  --radius-sm: 10px;

  --shadow-sm: 0 2px 8px rgba(42, 37, 33, 0.05);
  --shadow-md: 0 8px 20px rgba(42, 37, 33, 0.08);
  --shadow-accent: 0 8px 16px rgba(222, 94, 60, 0.35);

  --container: 1120px;
  --header-h: 64px;
}

/* --------------------------------------------------------------------------
   Podstawy
   -------------------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: 'Nunito Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, .logo, .brand {
  font-family: 'Nunito', system-ui, -apple-system, 'Segoe UI', sans-serif;
  letter-spacing: -0.015em;
  line-height: 1.25;
  margin: 0 0 12px;
  /* A long, space-less full name (rare, but the 120-char limit still
     allows one) must wrap instead of forcing horizontal overflow on a
     narrow phone width. */
  overflow-wrap: anywhere;
}

h1 { font-size: 26px; font-weight: 800; }
h2 { font-size: 20px; font-weight: 800; }
h3 { font-size: 17px; font-weight: 700; }
h4 { font-size: 15px; font-weight: 700; }

p { margin: 0 0 12px; }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-dark); }

img { max-width: 100%; height: auto; display: block; }

hr { border: 0; border-top: 1px solid var(--line); margin: 20px 0; }

.muted { color: var(--ink-soft); font-size: 13.5px; }
.small { font-size: 12.5px; }
.strong { font-weight: 700; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }
.stack > * + * { margin-top: 14px; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.container {
  width: 100%;
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 16px;
}

main.container { padding-top: 18px; padding-bottom: 48px; }

.page-head {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 12px; margin-bottom: 18px;
}
.page-head h1 { margin: 0; }

/* --------------------------------------------------------------------------
   Naglowek serwisu
   -------------------------------------------------------------------------- */

.site-header {
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 40;
}

.header-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; min-height: var(--header-h);
}

.logo {
  display: flex; align-items: center; gap: 8px;
  font-weight: 800; font-size: 19px; color: var(--brand-green); margin: 0;
  flex-shrink: 0;
}
.logo:hover { color: var(--brand-green); }
.logo span { color: var(--accent); }
.logo-mark { flex-shrink: 0; }

.main-nav { display: flex; align-items: center; gap: 0; }

.main-nav > a {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 8px 2px; border-radius: 999px;
  font-size: 14px; font-weight: 700; color: var(--brand-green);
  white-space: nowrap;
}
.main-nav > a:hover { background: var(--surface-2); color: var(--brand-green); }
.main-nav > a.active { color: var(--accent); background: var(--accent-soft); }

/* Phone-first: icon-only nav-icon-link (search, favorites, add listing,
   reservations, account name, language) - the header is too narrow on a
   phone for all of it spelled out. Tight padding/gap keeps six icon
   buttons from overflowing even on the narrowest supported phones
   (~360px). Desktop gets the full labels and roomier spacing back below. */
.main-nav .label { display: none; }
/* "Dodaj ogloszenie" (.label-short, only present inside that one link -
   see header.php) used to keep a short text label even on the narrowest
   phones (unlike Search/Reservations/Favorites/account/language, which
   read fine as icon-only) since it's the primary call to action - but the
   logo mark is now a wider illustrated image (public_html/img/logo-mark.png)
   instead of a small square SVG, and with that extra width the "+
   Inserieren" pill was exactly what pushed the header past 360px into
   horizontal overflow. Icon-only here too, matching the rest and the
   plain circular "+" in the brand mockup - desktop still gets the full
   .label below. */
.main-nav .label-short { display: none; }

/* Every icon-only slot (search, +, messages, favorites, account, language)
   reserves the SAME fixed square on phone widths, instead of each sizing
   to its own icon + padding - that's what made the bar look uneven: the
   account button carries an always-visible chevron the others don't (hidden
   below), and the unread badge on "messages" used to sit inline and widen
   only that one slot when there were unread notifications. */
.main-nav > a,
.main-nav .nav-username {
  width: 40px; height: 40px;
  padding: 0; margin: 0; gap: 0;
  justify-content: center;
}
.main-nav .nav-chevron { display: none; }
.notif-link .badge {
  position: absolute; top: 2px; right: 2px; margin-left: 0;
  min-width: 16px; height: 16px; padding: 0 4px; font-size: 10px;
}
@media (min-width: 900px) {
  .main-nav { gap: 2px; }
  .main-nav > a,
  .main-nav .nav-username {
    width: auto; height: auto;
    padding: 8px 10px; gap: 6px; justify-content: flex-start;
  }
  .main-nav .label { display: inline; }
  .main-nav .label-short { display: none; }
  .main-nav .nav-chevron { display: inline-flex; }
  .notif-link .badge {
    position: static; margin-left: 4px;
    min-width: 18px; height: 18px; padding: 0 5px; font-size: 11px;
  }
}

.btn-add {
  background: var(--accent) !important; color: var(--accent-ink) !important;
  box-shadow: var(--shadow-accent);
}
.btn-add:hover { background: var(--accent-dark) !important; }

/* Rozwijane menu konta i jezyka - czysty CSS, bez JavaScriptu */
.nav-account { position: relative; }
.nav-username {
  display: flex; align-items: center; gap: 6px;
  padding: 8px 12px; border-radius: 999px; cursor: default;
  font-size: 14px; font-weight: 700; color: var(--brand-green);
}
.nav-account:hover .nav-username { background: var(--surface-2); color: var(--brand-green); }

.nav-dropdown {
  display: none; position: absolute; right: 0; top: 100%;
  min-width: 190px; padding: 6px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-md); box-shadow: var(--shadow-md); z-index: 50;
}
.nav-account:hover .nav-dropdown,
.nav-account:focus-within .nav-dropdown { display: block; }

.nav-dropdown a,
.nav-dropdown button {
  display: block; width: 100%; text-align: left;
  padding: 9px 12px; border: 0; border-radius: var(--radius-sm);
  background: none; cursor: pointer;
  font: inherit; font-size: 13.5px; font-weight: 700; color: var(--ink);
}
.nav-dropdown a:hover, .nav-dropdown button:hover { background: var(--surface-2); }
.nav-dropdown a.active { color: var(--accent); background: var(--accent-soft); }
.nav-dropdown form { margin: 0; }

.notif-link { position: relative; }
.badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px; margin-left: 4px;
  border-radius: 999px; background: var(--accent); color: #fff;
  font-size: 11px; font-weight: 800;
}

/* --------------------------------------------------------------------------
   Przyciski, pola formularzy
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 11px 20px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--ink);
  font: inherit; font-size: 14px; font-weight: 700; text-align: center;
  cursor: pointer; transition: background .15s, border-color .15s, color .15s;
}
.btn:hover { background: var(--surface-2); color: var(--ink); }

.btn-primary {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
  box-shadow: var(--shadow-accent);
}
.btn-primary:hover { background: var(--accent-dark); border-color: var(--accent-dark); color: #fff; }

.btn-ghost { background: transparent; }
.btn-danger { color: var(--warn); border-color: #E9CFCA; background: var(--warn-bg); }
.btn-danger:hover { background: #F2D8D3; color: var(--warn); }
.btn-success { background: var(--free); border-color: var(--free); color: #fff; }
.btn-success:hover { background: #3E6A4D; color: #fff; }
.btn-block { display: flex; width: 100%; }
.btn-sm { padding: 7px 13px; font-size: 12.5px; }
.btn[disabled], .btn:disabled { opacity: .55; cursor: not-allowed; }

.btn-row { display: flex; flex-wrap: wrap; gap: 8px; }

/* A button that should read as plain clickable text, not a call to action -
   e.g. "delete account" on the profile page: deliberately understated, not
   competing visually with the actual save/primary buttons on the page. */
.btn-plain {
  display: inline; padding: 0; border: 0; border-radius: 0;
  background: none; font: inherit; font-weight: 600;
  text-decoration: underline; text-underline-offset: 2px;
  cursor: pointer;
}
.btn-plain:hover { background: none; text-decoration-thickness: 2px; }

.field { margin-bottom: 16px; }
/* :not(.toggle):not(.checkbox):not(.star-pick) - all three are also
   <label> elements nested inside a .field, and without this exclusion
   this rule's higher specificity (.field label = 0,1,1) was beating
   their own weaker rules (.toggle/.checkbox's `display: flex` at
   0,1,0; .star-pick's `color` at 0,1,1 tied but declared earlier):
   toggles/checkboxes fell out of their flex row and visually
   overlapped their text, and every rating star silently rendered in
   plain --ink instead of the picker's off/selected/hovered colors. */
.field label:not(.toggle):not(.checkbox):not(.star-pick),
.field-label {
  display: block; margin-bottom: 6px;
  font-size: 13px; font-weight: 700; color: var(--ink);
}
.field-hint { margin: 6px 0 0; font-size: 12.5px; color: var(--ink-soft); }

.alt-pickup-offer {
  margin-top: 12px; padding: 12px 14px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-md);
}
.alt-pickup-offer .field-hint { margin-top: 4px; }

input[type="text"], input[type="email"], input[type="password"],
input[type="number"], input[type="search"], input[type="tel"],
input[type="url"], select, textarea {
  width: 100%; padding: 11px 14px;
  border: 1px solid var(--line); border-radius: var(--radius-md);
  background: var(--surface); color: var(--ink);
  font: inherit; font-size: 15px;
}
textarea { min-height: 120px; resize: vertical; line-height: 1.55; }

input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--accent-soft); outline-offset: 0;
  border-color: var(--accent);
}

input[type="file"] { font-size: 13px; }

select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238A7A6B' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center;
  padding-right: 38px;
}

/* Profile phone field: a dial-code select next to the local-number input.
   select/input both inherit width:100% from the shared rule above, which -
   with flex-basis defaulting to that width - would make the select claim
   the whole row and squeeze the number field to nothing, so both get an
   explicit flex-basis here instead. */
.phone-field-row { display: flex; gap: 8px; }
.phone-field-row select { flex: 0 0 92px; width: auto; }

/* Price field: +/- 0.50 stepper buttons around the native number input.
   The native spin buttons are hidden (they'd still step by the input's
   own `step` attribute, which stays 0.01 so a manually typed value keeps
   full 2-decimal precision) - these two buttons are the only way the
   value steps by 0.50, both by click and via data-price-input's own
   ArrowUp/ArrowDown handling in app.js. */
.price-stepper { display: flex; align-items: center; gap: 8px; }
.price-stepper input[type="number"] { text-align: center; }
.price-stepper input[type="number"]::-webkit-inner-spin-button,
.price-stepper input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.price-stepper input[type="number"] { -moz-appearance: textfield; }
.price-stepper-btn {
  flex: 0 0 auto; width: 40px; height: 40px; padding: 0;
  border: 1px solid var(--line); border-radius: var(--radius-md);
  background: var(--surface); color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
}
.price-stepper-btn:hover:not(:disabled) { background: var(--surface-2); }
.price-stepper-btn:disabled { opacity: .5; cursor: not-allowed; }
.phone-field-row input[type="tel"] { flex: 1 1 0; width: auto; min-width: 0; }

.field-row { display: grid; gap: 12px; }
@media (min-width: 600px) {
  .field-row-2 { grid-template-columns: 1fr 1fr; }
  .field-row-3 { grid-template-columns: 1fr 1fr 1fr; }
}

.checkbox {
  display: flex; align-items: flex-start; gap: 9px;
  font-size: 14px; font-weight: 600; cursor: pointer;
}
.checkbox input { width: auto; margin: 3px 0 0; flex-shrink: 0; }

.lang-checkbox-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 14px;
}
@media (min-width: 560px) { .lang-checkbox-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.lang-checkbox { font-weight: 400; font-size: 13.5px; align-items: center; }

/* The two profile checkbox lists (spoken languages, preferred contact
   method) read as toggle switches rather than checkboxes - a plain
   sliding on/off track, still a real <input type="checkbox"> underneath
   (keyboard/screen-reader behavior unchanged), just restyled. */
.lang-checkbox input[type="checkbox"] {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  position: relative; width: 32px; height: 18px; margin: 0; flex-shrink: 0;
  border-radius: 999px; border: 1px solid var(--line); background: var(--line);
  cursor: pointer; transition: background .15s, border-color .15s;
}
.lang-checkbox input[type="checkbox"]::before {
  content: ''; position: absolute; top: 1px; left: 1px;
  width: 14px; height: 14px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgba(42, 37, 33, .3);
  transition: transform .15s;
}
.lang-checkbox input[type="checkbox"]:checked {
  background: var(--accent); border-color: var(--accent);
}
.lang-checkbox input[type="checkbox"]:checked::before { transform: translateX(14px); }
.lang-checkbox input[type="checkbox"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.lang-checkbox input[type="checkbox"]:disabled { opacity: .5; cursor: not-allowed; }
.lang-checkbox:has(input:disabled) { opacity: .6; cursor: not-allowed; }

/* Przelacznik wlacz/wylacz (ustawienia, kategorie) */
.toggle { position: relative; display: flex; align-items: center; gap: 10px; cursor: pointer; }
.toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
.toggle-track {
  width: 42px; height: 24px; border-radius: 999px; flex-shrink: 0;
  background: var(--surface-2); border: 1px solid var(--line);
  position: relative; transition: background .15s, border-color .15s;
}
.toggle-track::after {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--surface); box-shadow: var(--shadow-sm);
  transition: transform .15s;
}
.toggle input:checked + .toggle-track { background: var(--free); border-color: var(--free); }
.toggle input:checked + .toggle-track::after { transform: translateX(18px); }
.toggle input:focus-visible + .toggle-track { outline: 2px solid var(--accent); outline-offset: 2px; }
.toggle-text { font-size: 13.5px; font-weight: 700; }

/* --------------------------------------------------------------------------
   Chipy (kategorie, rozmiary, zustand)
   -------------------------------------------------------------------------- */

.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chips-scroll {
  flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px;
  scrollbar-width: thin; -webkit-overflow-scrolling: touch;
}
.chips-scroll .chip { flex-shrink: 0; }

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 15px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--ink-soft);
  font-size: 13.5px; font-weight: 700; cursor: pointer; white-space: nowrap;
}
/* .chip's own display:inline-flex above has the same specificity as the
   browser's built-in [hidden]{display:none} rule and comes later in the
   cascade, so it wins - a hidden chip would otherwise still render (see
   .tree-picker-row[hidden] elsewhere in this file for the same fix). */
.chip[hidden] { display: none; }
.chip:hover { background: var(--surface-2); color: var(--ink); }
.chip.active { background: var(--ink); border-color: var(--ink); color: #fff; }
.chip.active:hover { background: var(--ink); color: #fff; }
/* A one-shot chip (e.g. the "worth adding" content-hint chips,
   views/listings/form.php) disables itself once clicked, via .active
   above for the filled look - just needs the pointer cursor turned off. */
.chip:disabled { cursor: default; }
.chip-sm { padding: 6px 12px; font-size: 12.5px; }

/* Chip realizowany jako radio - wybor bez JavaScriptu */
.chip-radio { position: relative; }
.chip-radio input { position: absolute; opacity: 0; width: 0; height: 0; }
.chip-radio input:checked + .chip { background: var(--ink); border-color: var(--ink); color: #fff; }
.chip-radio input:focus-visible + .chip { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Podpowiedzi czatu (zawsze po niemiecku) + opcjonalne tlumaczenie podgladowe.
   Pelna zdaniowa tresc nie pasuje do ksztaltu okraglej "pigulki" (.chip
   domyslnie ma border-radius: 999px, pomyslane dla krotkich,
   jednowierszowych etykiet) - stad wlasny, luzniejszy ksztalt i pelna
   szerokosc zamiast zawijania w rzad obok siebie. */
.quick-replies .chips { flex-direction: column; align-items: stretch; }
.quick-reply-note { margin: 0 0 6px; }
.quick-reply-chip {
  width: 100%; border-radius: var(--radius-md);
  flex-direction: column; align-items: flex-start; gap: 3px;
  text-align: left; white-space: normal; line-height: 1.4;
}
.quick-reply-preview {
  font-weight: 400; font-style: italic; color: var(--ink-soft); font-size: 12px;
}
.chip.quick-reply-chip.active .quick-reply-preview { color: rgba(255,255,255,0.75); }

.tag {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 9px; border-radius: 7px;
  font-size: 11px; font-weight: 800; letter-spacing: .02em;
  background: var(--surface-2); color: var(--ink-soft);
}
.tag-free { background: var(--free-bg); color: var(--free); }
.tag-promo { background: var(--promo-bg); color: var(--promo); }
.tag-warn { background: var(--warn-bg); color: var(--warn); }
.tag-accent { background: var(--accent-soft); color: var(--accent); }
/* Reserved/sold status, both on the card overlay (.tag-*) and the listing
   page's own status chip (.chip-status-*) - deliberately solid, high-
   contrast fills, not the pale .tag-warn/.tag-accent tints above: a listing
   already gone is exactly the fact a buyer must not miss at a glance, so it
   gets the same visual weight as .tag-promo/.tag-free rather than reading
   as a soft, easy-to-miss caption. "Reserved" (still in progress) uses the
   brand accent; "sold" and every other non-active status (expired, hidden,
   archived) use solid ink - definitively over, not just pending. */
.tag-reserved, .chip-status-reserved { background: var(--accent); color: #fff; }
.tag-sold, .chip-status-sold { background: var(--ink); color: #fff; }
.chip-status-reserved { border-color: var(--accent); }
.chip-status-sold { border-color: var(--ink); }

.size-pill {
  display: inline-block; padding: 2px 8px; border-radius: 6px;
  background: var(--surface-2); color: var(--ink-soft);
  font-size: 11px; font-weight: 700;
}

/* --------------------------------------------------------------------------
   Karty i siatka ogloszen
   -------------------------------------------------------------------------- */

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
}
.card-pad { padding: 18px; }

.grid {
  display: grid; gap: 14px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 640px) { .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; } }
@media (min-width: 900px) { .grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.listing-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); overflow: hidden;
  display: flex; flex-direction: column; position: relative;
  transition: box-shadow .15s, transform .15s;
}
.listing-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }

.listing-card .photo {
  position: relative; aspect-ratio: 4 / 3;
  background: var(--surface-2);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.listing-card .photo img { width: 100%; height: 100%; object-fit: cover; }
.listing-card .photo .placeholder { color: #C9B9A6; }

.listing-card .photo .tag { position: absolute; top: 10px; left: 10px; }
/* A reserved/sold card's photo dims AND desaturates, on top of its
   .tag-reserved/.tag-sold badge above - the badge alone is easy to skim
   past in a grid of otherwise-identical thumbnails; a merely-dimmed photo
   (tried first at opacity .55, still too subtle in practice) still reads
   as a normal, colorful listing. Grayscale removes exactly what makes a
   thumbnail "pop" in a scanning grid, so it's the first thing the eye
   registers as different, with the opacity drop as a second, reinforcing
   cue. Same treatment on the listing page's own full photo - see
   .listing-photo.is-unavailable below. */
.listing-card.is-unavailable .photo img { opacity: .45; filter: grayscale(.85); }

.fav-btn {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  width: 34px; height: 34px; padding: 0;
  border: 0; border-radius: 50%; background: rgba(255, 255, 255, .92);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; box-shadow: var(--shadow-sm);
}
.fav-btn:hover { background: #fff; }
.fav-btn svg { stroke: var(--ink); }
.fav-btn.is-fav svg { fill: var(--accent); stroke: var(--accent); }
/* Remove-from-favorites buttons on lists that only ever show already-
   favorited items (e.g. the favorite sellers tab) - the heart should
   read as "favorited" (filled) rather than an empty add-to-favorites one. */
.btn.is-fav svg { fill: var(--accent); stroke: var(--accent); }
.fav-form {
  position: absolute; top: 8px; right: 8px; z-index: 2; margin: 0;
}
.fav-form .fav-btn { position: static; }
/* The count of people favoriting this listing, as a badge on the heart
   button's own top-right corner - on the card thumbnail and, at a larger
   size, on the listing page's main photo. */
.fav-count {
  position: absolute; top: -4px; right: -4px; z-index: 3;
  background: var(--accent); color: #fff; font-weight: 700;
  border-radius: 999px; box-shadow: var(--shadow-sm); line-height: 1;
  font-size: 10px; padding: 2px 5px; min-width: 15px; text-align: center;
  border: 1.5px solid #fff;
}

.listing-card .body {
  padding: 10px 12px 13px;
  display: flex; flex-direction: column; gap: 4px; flex-grow: 1;
}
.listing-card .body .size-pill { align-self: flex-start; }
.listing-card .title {
  font-size: 13.5px; font-weight: 700; color: var(--ink); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.listing-card .price { font-size: 16px; font-weight: 800; margin-top: auto; }
.listing-card .price.is-free { color: var(--free); }
.listing-card .price s { color: var(--ink-soft); font-size: 12px; font-weight: 600; margin-left: 5px; }
.listing-card .meta {
  display: flex; align-items: center; gap: 4px;
  font-size: 11.5px; color: var(--ink-soft);
}
.listing-card .stretch::after {
  content: ''; position: absolute; inset: 0; z-index: 1;
}

/* Pasek miniatur (inne ogloszenia sprzedajacego) - poziomy scroll bez
   widocznego paska przewijania (ukryty na wszystkich silnikach), za to
   z scroll-snap, zeby przeciaganie/swipe zatrzymywalo sie ladnie na
   kolejnej miniaturce zamiast w polowie kafelka. */
.thumb-row {
  display: flex; gap: 10px; overflow-x: auto; padding-bottom: 2px;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.thumb-row::-webkit-scrollbar { display: none; height: 0; }
.thumb-row a { flex-shrink: 0; width: 92px; scroll-snap-align: start; }
.thumb-row .thumb {
  display: block; width: 92px; height: 92px; border-radius: var(--radius-md);
  background: var(--surface-2); overflow: hidden; border: 1px solid var(--line);
}
.thumb-row .thumb img { width: 100%; height: 100%; object-fit: cover; }
.thumb-row .thumb-price {
  display: block; font-size: 11.5px; font-weight: 700; color: var(--ink); margin-top: 4px;
}

/* --------------------------------------------------------------------------
   Komunikaty, puste stany, paginacja
   -------------------------------------------------------------------------- */

.flash {
  padding: 12px 16px; border-radius: var(--radius-md); margin-bottom: 16px;
  font-size: 14px; font-weight: 600; border: 1px solid transparent;
}
.flash-success { background: var(--free-bg); color: var(--free); border-color: #CFE3CF; }
.flash-error { background: var(--warn-bg); color: var(--warn); border-color: #E9CFCA; }
.flash-info { background: var(--accent-soft); color: var(--accent-dark); border-color: #F3D4C8; }

.empty-state {
  text-align: center; padding: 48px 20px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.empty-state h1, .empty-state h2 { margin-bottom: 8px; }
.empty-state .btn { margin-top: 14px; }

.pagination {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  margin-top: 28px;
}
.pagination .page-info { font-size: 13px; font-weight: 700; color: var(--ink-soft); }

/* --------------------------------------------------------------------------
   Strona glowna
   -------------------------------------------------------------------------- */

.hero {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 26px 20px; margin-bottom: 26px;
  text-align: center;
}
.hero h1 { font-size: 26px; margin-bottom: 8px; }
.hero p { color: var(--ink-soft); max-width: 46ch; margin: 0 auto 18px; }
@media (min-width: 720px) { .hero { padding: 40px 32px; } .hero h1 { font-size: 34px; } }

/* Explicit rows - the free-text search on its own line, the postal
   code/city/radius fields together underneath (wrapping to more than one
   line on narrow screens), the submit button on its own line below that -
   rather than one flex row that just happens to wrap wherever space runs
   out. */
.search-bar { display: flex; flex-direction: column; gap: 8px; max-width: 620px; margin: 0 auto; }
.search-bar-row { display: flex; flex-wrap: wrap; gap: 8px; }
.search-bar-row input[type="search"] { flex: 1 1 auto; width: 100%; }
.search-bar-row-secondary input[type="text"] { flex: 1 1 110px; min-width: 90px; }
.search-bar-row-secondary select { flex: 1 1 110px; min-width: 90px; }

.category-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 640px) { .category-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 900px) { .category-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); } }

.category-tile {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 18px 10px; text-align: center;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); color: var(--ink);
  font-size: 13.5px; font-weight: 700;
}
.category-tile:hover { background: var(--accent-soft); border-color: var(--accent-soft); color: var(--accent); }
.category-tile .icon {
  width: 44px; height: 44px; border-radius: 13px; background: var(--surface-2);
  display: flex; align-items: center; justify-content: center; color: var(--accent);
}
.category-tile:hover .icon { background: #fff; }

.section { margin-bottom: 32px; }
.section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin-bottom: 14px;
}
.section-head h2 { margin: 0; }
.section-head a { font-size: 13.5px; font-weight: 700; }

.cta-band {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 16px; padding: 22px; margin-bottom: 32px;
  background: var(--accent-soft); border-radius: var(--radius-lg);
}
.cta-band h2 { margin: 0 0 4px; }
.cta-band p { margin: 0; color: var(--ink-soft); }

/* --------------------------------------------------------------------------
   Wyszukiwarka: uklad z panelem filtrow
   -------------------------------------------------------------------------- */

.search-layout { display: flex; flex-direction: column; gap: 14px; }

/* /anzeigen's whole filter set is one <form class="search-bar"> now - the
   always-visible query/postal-code/radius bar (same markup and classes as
   the homepage's, see above) followed by the collapsible "Filtry" section
   below it (category, price, condition, photo) and a plain clear-filters
   link - no width cap here, unlike the homepage's centered bar, since this
   one needs the full content column. Margin must be reset too, not just
   max-width: .search-bar's "margin: 0 auto" was meant to center a capped
   620px box, but on a flex item (.search-layout is display:flex), auto
   side margins also override the default cross-axis stretch - so left as
   "0 auto" once uncapped, the form was shrinking to a fit-content width
   instead of filling the column like every other element below it. */
#filters-form { max-width: none; margin: 0; }

.sort-form { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.sort-form select { width: auto; min-width: 180px; padding: 8px 34px 8px 12px; font-size: 13px; }

/* The filter fields themselves are no longer grouped inside a bordered
   "card" - each one is its own white bordered box (same look as the
   search bar's q/plz/city/radius fields above), simply laid out in a
   wrapping flex row. flex-wrap alone gives the "one line where there's
   room, stacks on narrow screens" behavior for free, with no separate
   desktop/mobile CSS branch needed. align-items: flex-start is required,
   not just nice-to-have: every field here is a different natural height
   (a closed tree-picker summary vs. a bordered checkbox box), and the
   default flex stretch would otherwise blow every field in the row up to
   match the tallest one - most visibly whichever picker's OPEN dropdown
   panel happens to be tall, since that panel is part of the flex item's
   own box (position: absolute doesn't take it out of the item's content
   for height purposes here) and so briefly becomes the row's tallest
   item while open. */
.filters-row { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px; }
.filters-row > .tree-picker { flex: 1 1 170px; min-width: 150px; }
.filters-row > input[type="number"] { flex: 1 1 90px; min-width: 80px; width: auto; }

/* Free-text checkbox filters (free_only, has_photo) get the same white
   bordered-box treatment as everything else in the row, instead of the
   plain unboxed .checkbox used inside regular forms. */
.filter-check {
  display: flex; align-items: center; gap: 8px; flex: 0 0 auto;
  padding: 11px 14px; border: 1px solid var(--line); border-radius: var(--radius-md);
  background: var(--surface); font-size: 14px; font-weight: 600; cursor: pointer;
  white-space: nowrap;
}
.filter-check input { width: auto; flex-shrink: 0; }

/* Plain, unstyled "Wyczyść filtry" link under the filters panel - no
   button chrome, replacing what used to be a "Filtruj"/"Pokaż ogłoszenia"
   submit button. Every control in the form auto-submits on its own change
   (app.js) instead. */
.filters-clear { margin: 10px 2px 0; font-size: 13.5px; }
.filters-clear a { color: var(--ink-soft); text-decoration: underline; }
.filters-clear a:hover { color: var(--ink); }

/* Category tree / size / condition multi-select: a <details> "select"
   whose panel is an absolutely-positioned dropdown rather than pushing
   the rest of the page down - reused for all three pickers, the category
   one nesting a second <details> per root category with children for its
   subcategories.

   The base .tree-picker-panel is deliberately NOT scrollable/height-
   capped itself (unlike a plain single-level select dropdown might be):
   for the CATEGORY picker specifically, a root's subcategories open as
   their own flyout anchored to that row (see .tree-picker-children
   below), and an element with overflow: auto/hidden clips ANY positioned
   descendant that would otherwise render outside its box - even one
   nested several levels down - so if this panel scrolled, the flyout
   would get clipped to it and never be visible. Verified this
   empirically (headless Chromium, several structurally-identical repros)
   after an initial attempt that assumed otherwise.

   Size and condition have no such flyout, so nothing stops THEM scrolling
   - and with categories like clothing running 20+ sizes, an uncapped
   panel there would spill the whole page downward. Those two add the
   -scroll modifier below; category leaves the panel uncapped. */
.tree-picker { position: relative; }
.tree-picker > summary {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 11px 14px; border: 1px solid var(--line); border-radius: var(--radius-md);
  background: var(--surface); font-size: 14px; cursor: pointer; list-style: none;
}
.tree-picker > summary::-webkit-details-marker { display: none; }
.tree-picker > summary span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tree-picker[open] > summary { border-color: var(--accent); }
.tree-picker[open] > summary svg { transform: rotate(180deg); }
.tree-picker > summary svg { transition: transform 0.15s ease; flex-shrink: 0; }
.tree-picker-panel {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 20;
  padding: 6px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}
.tree-picker-panel-scroll { max-height: 320px; overflow-y: auto; }
/* Seller-language filter: a plain text input pinned to the top of its
   panel (sticky, so it stays reachable while scrolling ~39 languages)
   that narrows the checkbox rows below as you type - app.js. */
input.lang-search {
  position: sticky; top: 0; z-index: 1; margin-bottom: 4px;
  padding: 8px 10px; font-size: 13.5px;
  background: var(--surface);
}
.tree-picker-row[hidden] { display: none; }
.tree-picker-row {
  display: flex; align-items: center; justify-content: flex-start; gap: 8px;
  padding: 9px 10px; border-radius: var(--radius-sm); font-size: 14px; cursor: pointer;
}
.tree-picker-row:hover { background: var(--surface-2); }
.tree-picker-row label { display: flex; align-items: center; gap: 8px; cursor: pointer; flex: 1 1 auto; min-width: 0; }
/* Category/subcategory rows select via the whole row - no visible radio
   circle. Still a real <input type="radio"> underneath (that's what
   actually gets submitted), just hidden the same way as .visually-hidden
   rather than display:none, so the wrapping <label> still forwards
   clicks/taps to it and it stays reachable by keyboard/Tab. Condition and
   size use checkboxes in this same .tree-picker-row, deliberately left
   alone by this selector (type="radio" only matches category/subcategory). */
.tree-picker-row input[type="radio"] {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* A root category with subcategories opens them as their own anchored
   flyout panel next to it (position: relative on the branch, the panel
   itself position: absolute against it) rather than expanding inline in
   the same list - matches a typical two-level "mega menu" rather than an
   indented accordion. The `name="category-branch"` attribute on every
   branch (native <details> exclusive-group support) keeps only one
   flyout open at a time with no JS. */
.tree-picker-branch { position: relative; border: 0; }
.tree-picker-branch > summary {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 9px 10px; cursor: pointer; list-style: none;
}
.tree-picker-branch > summary::-webkit-details-marker { display: none; }
.tree-picker-branch > summary:hover { background: var(--surface-2); border-radius: var(--radius-sm); }
.tree-picker-branch[open] > summary { background: var(--surface-2); border-radius: var(--radius-sm); }
.tree-picker-children {
  /* left: 100% with NO margin - the flyout's own box must start exactly
     where the row's box ends, flush, with zero gap between them. A gap
     here (this used to be margin-left: 6px) is dead space that belongs to
     neither the row nor the flyout: a mouse crossing it diagonally toward
     a subcategory fires mouseleave on .tree-picker-branch (app.js) before
     ever entering the flyout, closing it mid-hover. Confirmed empirically
     (headless Chromium, stepped mouse-move) as the cause of "flyout closes
     before I can click a subcategory". */
  position: absolute; top: 0; left: 100%; z-index: 21;
  width: 220px; max-height: 320px; overflow-y: auto; padding: 6px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}
/* Not enough room to the side on a phone - falls back to a plain indented
   list under the row instead of a floating panel that could run off
   screen. */
@media (max-width: 640px) {
  .tree-picker-children {
    position: static; margin-left: 18px; width: auto;
    border: 0; box-shadow: none; padding: 0;
  }
}

/* <details> starts with no `open` attribute - collapsed by default, native
   toggle on tapping <summary>, exactly like any other disclosure widget.
   Desktop wants the row visible by default instead - app.js force-opens
   this element once on load past the 900px breakpoint (there's no
   CSS-only way to default a <details> open) and re-checks on resize. The
   summary itself is plain text (not a boxed button) and stays visible
   and clickable at every width, so it keeps working as a manual
   collapse/expand toggle even once auto-opened on desktop. */
.filters-toggle > summary {
  display: flex; align-items: center; gap: 6px;
  font-size: 15px; font-weight: 700; color: var(--ink); cursor: pointer;
  list-style: none;
}
.filters-toggle > summary::-webkit-details-marker { display: none; }
.filters-toggle > summary:hover { color: var(--accent); }
.filters-toggle > summary svg { transition: transform 0.15s ease; }
.filters-toggle[open] > summary svg { transform: rotate(180deg); }
.filters-toggle[open] > summary { margin-bottom: 10px; }

.results-head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 14px;
}
.results-count { font-size: 13.5px; font-weight: 700; color: var(--ink-soft); }

/* Active-filter chips (SearchFilters::activeChips()) under the toolbar row -
   each one a pill with an "x" that reloads with just that filter cleared. */
.active-filters { display: flex; flex-wrap: wrap; gap: 8px; margin-top: -4px; }
.chip-removable {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 10px; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent-dark);
  font-size: 12.5px; font-weight: 700;
}
.chip-removable:hover { background: var(--accent); color: #fff; }

.seller-banner {
  display: flex; align-items: center; gap: 14px;
  padding: 16px 18px; margin-bottom: 18px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
}

/* --------------------------------------------------------------------------
   Szczegoly ogloszenia
   -------------------------------------------------------------------------- */

/* minmax(0, 1fr), not the "no grid-template-columns" default: with no
   explicit track, a CSS grid sizes its single implicit column to fit its
   content's max-content width rather than the container - so anything
   inside .listing-side that doesn't wrap (e.g. the seller's other-listings
   thumbnail strip, itself independently scrollable) was stretching the
   WHOLE sidebar, and with it the whole page, out to that content width
   and forcing a page-wide horizontal scrollbar on every phone screen. */
.listing-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px; }
@media (min-width: 900px) {
  /* Capped (rather than a flexible 1.5fr) so the main column's own width
     tracks the 4:3 photo's width below - a fluid column left the photo
     (deliberately capped, see .listing-photo below) floating with a large
     empty gap before the sidebar; capping the column itself means
     title/price/description sit at that same width, right next to the
     sidebar, with only the grid's own gap between them. The cap itself is
     a custom property (480px fallback for no-JS) - app.js's own section on
     this recomputes it from the seller card's actual rendered height,
     which varies a lot with its content (verified badge, activity,
     sold/bought, languages, other listings), so a single fixed number
     here only ever matched one particular case. */
  .listing-layout { grid-template-columns: minmax(0, var(--listing-photo-col, 480px)) 340px; align-items: start; }
  .listing-side { position: sticky; top: calc(var(--header-h) + 16px); }
}

.listing-photo {
  position: relative; border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid var(--line); background: var(--surface-2);
  aspect-ratio: 4 / 3;
  display: flex; align-items: center; justify-content: center;
}
.listing-photo img { width: 100%; height: 100%; object-fit: cover; }
/* Same reserved/sold treatment as .listing-card.is-unavailable above -
   the full-size photo on the listing's own page was the one place this
   status wasn't visible at all before, only the status chip below it was. */
.listing-photo.is-unavailable img { opacity: .45; filter: grayscale(.85); }
/* The <a> wrapping the photo (opens the fullscreen zoom view) needs to be
   a block filling the same box as the img used to fill on its own -
   otherwise, as an inline element, it wouldn't give the img's width/
   height: 100% a proper block containing box to resolve against. */
.listing-photo > a { display: block; width: 100%; height: 100%; cursor: zoom-in; }
.listing-photo .tag { position: absolute; top: 12px; left: 12px; }
.listing-photo .photo-count {
  position: absolute; bottom: 12px; right: 12px;
  padding: 4px 10px; border-radius: 999px;
  background: rgba(42, 37, 33, .68); color: #fff;
  font-size: 11.5px; font-weight: 700;
}

/* Share + favorite, overlaid on the main photo's top-right corner - share
   first, then favorite (with its count) - instead of separate full-width
   buttons in the sidebar. */
.photo-overlay-actions {
  position: absolute; top: 12px; right: 12px; z-index: 2;
  display: flex; align-items: center; gap: 6px;
}
.photo-overlay-actions form { margin: 0; position: relative; }
.photo-icon-btn-wrap { position: relative; display: inline-flex; }
.photo-icon-btn {
  width: 36px; height: 36px; padding: 0; border: 0; border-radius: 50%;
  background: rgba(255, 255, 255, .92); color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; box-shadow: var(--shadow-sm);
}
.photo-icon-btn:hover { background: #fff; }
.photo-icon-btn.is-fav svg { fill: var(--accent); stroke: var(--accent); }
/* "Link copied" feedback for the icon-only share button (no text label to
   swap, unlike the old full-width button) - app.js sets data-copied. */
.photo-icon-btn[data-copied] { position: relative; }
.photo-icon-btn[data-copied]::after {
  content: attr(data-copied);
  position: absolute; top: 110%; right: 0; white-space: nowrap;
  background: rgba(42, 37, 33, .92); color: #fff;
  font-size: 11px; font-weight: 600; padding: 4px 9px; border-radius: 6px;
  box-shadow: var(--shadow-sm);
}

.breadcrumb {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  font-size: 12.5px; font-weight: 700; color: var(--ink-soft); margin-bottom: 8px;
}
.breadcrumb a { color: var(--ink-soft); }
.breadcrumb a:hover { color: var(--accent); }
.breadcrumb .sep { color: #C9B9A6; }

.listing-price { font-size: 28px; font-weight: 800; margin: 4px 0 10px; }
.listing-price.is-free { color: var(--free); }
.listing-price s { font-size: 16px; font-weight: 600; color: var(--ink-soft); margin-left: 8px; }

.listing-facts {
  display: flex; flex-wrap: wrap; gap: 14px;
  padding: 12px 0; margin: 12px 0;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  font-size: 12.5px; color: var(--ink-soft); font-weight: 600;
}
.listing-facts span { display: inline-flex; align-items: center; gap: 5px; }

.description { white-space: pre-line; line-height: 1.65; }

.seller-card {
  display: flex; flex-direction: column; gap: 12px;
  padding: 18px; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.seller-head { display: flex; align-items: center; gap: 12px; }

.avatar {
  width: 48px; height: 48px; border-radius: 50%; flex-shrink: 0;
  background: #F0D9A8; color: #8A5A20; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  font-family: 'Nunito', sans-serif; font-weight: 800; font-size: 15px;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar-lg { width: 84px; height: 84px; font-size: 26px; }
.avatar-sm { width: 34px; height: 34px; font-size: 12px; }

/* "Active today" marker (App\Services\ActivityStatus) - a plain colored
   dot, the same weight as a green "online now" indicator elsewhere. */
.activity-dot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  background: var(--free); margin-right: 2px; vertical-align: 1px;
}

.verify-badges { display: flex; flex-wrap: wrap; gap: 6px; }
.verify-badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 9px; border-radius: 999px; border: 0;
  background: var(--free-bg); color: var(--free);
  font-size: 11.5px; font-weight: 700; font-family: inherit; cursor: pointer;
  position: relative;
}
/* Shown instead of the real (green) badge above when phone verification
   is enabled site-wide but this particular seller hasn't passed it -
   muted/neutral on purpose, so it reads as "incomplete", not as another
   flavor of "verified". */
.verify-badge-incomplete { background: var(--surface-2); color: var(--ink-soft); }
/* Tap/click reveals which checks passed (app.js toggles data-tooltip-open) -
   a plain title attribute doesn't show at all on a touch tap. */
.verify-badge[data-tooltip-open]::after {
  content: attr(data-tooltip);
  position: absolute; top: 130%; left: 0; z-index: 5; white-space: nowrap;
  background: rgba(42, 37, 33, .92); color: #fff;
  font-size: 11px; font-weight: 600; padding: 4px 9px; border-radius: 6px;
  box-shadow: var(--shadow-sm);
}

/* Spoken-languages self-tags on a public profile - deliberately understated
   (small, muted), not another verification-style badge. The label matches
   the surrounding muted text instead of calling attention to itself. */
.lang-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.lang-tags-label { font: inherit; color: inherit; }
/* A hand-drawn SVG country flag (App\Core\Flags) - never an emoji, so it
   always renders as a picture regardless of the device's emoji font. */
.lang-tag {
  display: inline-flex; line-height: 0; cursor: default;
  border-radius: 3px; overflow: hidden; box-shadow: 0 0 0 1px var(--line);
}
.flag-svg { display: block; }
.lang-checkbox .flag-svg { display: inline-block; vertical-align: -3px; margin-right: 4px; border-radius: 2px; box-shadow: 0 0 0 1px var(--line); }
/* Preferred-contact-method checkboxes (views/account/profile.php) reuse
   .lang-checkbox for the same 2/3-column grid, with a plain icon instead
   of a flag - just needs its own gap from the label text. */
.lang-checkbox .icon-inline { margin-right: 4px; color: var(--ink-soft); }

.rating-inline {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 13px; font-weight: 700; color: var(--ink);
}
.stars { display: inline-flex; gap: 1px; color: var(--promo); }
.stars svg { fill: currentColor; stroke: none; }
.stars .off { color: #DED2C2; }

/* Clickable 1-5 star picker (rating form). Radios are rendered value 5..1
   in DOM order and the row is flipped with row-reverse so star 1 lands on
   the left. Which stars are lit is driven entirely by app.js toggling
   .selected (the current pick) / .hovered (a mouse preview of a pick) -
   not by ":checked ~ label", so it's identical on a tap (touch has no
   hover at all) and a mouse, and doesn't depend on sibling-combinator
   hover quirks. The ":checked ~ label" rule stays as a no-JS fallback. */
.star-picker { display: inline-flex; flex-direction: row-reverse; gap: 2px; }
.star-picker input { position: absolute; opacity: 0; width: 0; height: 0; }
.star-picker label { cursor: pointer; color: #DED2C2; line-height: 0; }
.star-picker label svg { fill: currentColor; stroke: none; }
.star-picker input:checked ~ label,
.star-picker input:checked ~ label ~ label,
.star-picker label.selected,
.star-picker label.hovered {
  color: var(--promo);
}
.star-picker input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

/* Inline "you missed this field" message (rating form) - the star radios
   are visually hidden, so the browser's native required-field bubble has
   nothing to anchor to and silently blocks the submit instead of showing
   anything; this is the visible replacement for both that and an empty
   comment. */
.field-error {
  margin: 6px 0 0; font-size: 12.5px; font-weight: 700; color: var(--warn);
}
.field.is-invalid .star-picker { outline: 2px solid var(--warn); outline-offset: 4px; border-radius: 6px; }
.field.is-invalid textarea { border-color: var(--warn); }

.sticky-cta {
  position: sticky; bottom: 0; z-index: 30;
  display: flex; gap: 10px; padding: 12px 16px;
  margin: 20px -16px -48px;
  background: var(--surface); border-top: 1px solid var(--line);
}
.sticky-cta .btn { flex: 1; }
@media (min-width: 900px) {
  /* Un-stickied here (position: static), but still needs its own top
     margin - .seller-card above it has no bottom margin of its own, so
     without this the message/reserve buttons sat flush against the
     "other listings" thumbnail row with no visible gap. */
  .sticky-cta { position: static; margin: 16px 0 0; padding: 0; border: 0; background: none; }
}

/* Floating "Save" bar (profile form) - pinned to the viewport bottom
   regardless of scroll position, not just within its own container (see
   .sticky-cta above for that narrower kind). Its button targets the form
   via form="profile-form" since it lives outside the <form> itself. Full
   width on phones (a single small pill on an otherwise-empty bar read as
   an accident, not a call to action); a normal right-aligned button once
   there's room for it not to dominate the row. */
.form-float-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  background: var(--surface); border-top: 1px solid var(--line);
  box-shadow: var(--shadow-md);
}
.form-float-bar-inner { display: flex; padding: 10px 16px; }
.form-float-bar .btn { flex: 1; padding-top: 13px; padding-bottom: 13px; font-size: 15px; }
@media (min-width: 640px) {
  .form-float-bar-inner { justify-content: flex-end; }
  .form-float-bar .btn { flex: none; min-width: 180px; }
}
main.has-float-bar { padding-bottom: 84px; }

/* Simple confirmation popup (e.g. account deletion) - see app.js section
   "modal". Content is plain form markup, same .field/.btn styles as
   everywhere else. */
.modal-overlay {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
  background: rgba(42, 37, 33, 0.45);
}
.modal-overlay[hidden] { display: none; }
.modal-box {
  width: 100%; max-width: 380px;
  background: var(--surface); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md); padding: 20px;
}
.modal-box h2 { margin-top: 0; }
.modal-actions { display: flex; gap: 10px; margin-top: 16px; }
.modal-actions .btn { flex: 1; }

/* Fullscreen photo viewer (views/listings/show.php) - a dark, edge-to-edge
   variant of .modal-overlay rather than the usual small white card, with
   its own zoom/pan handling in app.js. */
.photo-lightbox { padding: 0; background: rgba(20, 16, 12, 0.94); }
.lightbox-stage {
  position: relative; width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  /* Hands ALL touch gestures on the image to our own pointer-event
     handling (drag to pan, pinch to zoom) instead of the browser's
     native scroll/pinch, which would otherwise fight with it. */
  touch-action: none;
}
.lightbox-stage img {
  max-width: 100%; max-height: 100%; object-fit: contain;
  cursor: zoom-in; user-select: none; -webkit-user-drag: none;
  transition: transform 0.2s ease;
}
.lightbox-stage img.is-zoomed { cursor: zoom-out; }
/* No transition while actively dragging/pinching - the image must track
   the pointer 1:1, not ease toward it a beat behind. */
.lightbox-stage img.is-dragging { transition: none; cursor: grabbing; }
.lightbox-close {
  position: fixed; top: 16px; right: 16px; z-index: 2;
  width: 40px; height: 40px; padding: 0; border: 0; border-radius: 50%;
  background: rgba(255, 255, 255, .14); color: #fff;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.lightbox-close:hover { background: rgba(255, 255, 255, .24); }

details.report-box { margin-top: 18px; }
details.report-box > summary {
  font-size: 12.5px; font-weight: 700; color: var(--ink-soft); cursor: pointer;
}
details.report-box > summary:hover { color: var(--warn); }
details.report-box > div { margin-top: 12px; }

/* "More about seller" (/verkaeufer/{id}) - a plain <details>, same idiom
   as the report-box above. */
.seller-more { margin-top: 8px; }
.seller-more > summary {
  font-size: 12.5px; font-weight: 700; color: var(--accent); cursor: pointer;
  list-style: none;
}
.seller-more > summary::-webkit-details-marker { display: none; }
.seller-more > summary:hover { color: var(--accent-dark); }
.seller-more > div {
  margin-top: 10px; padding: 12px 14px;
  background: var(--surface-2); border-radius: var(--radius-md);
}

/* --------------------------------------------------------------------------
   Formularz ogloszenia
   -------------------------------------------------------------------------- */

.form-narrow { max-width: 640px; margin: 0 auto; }

.photo-drop {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; padding: 26px 18px; text-align: center;
  border: 2px dashed var(--line); border-radius: var(--radius-lg);
  background: var(--surface); color: var(--ink-soft);
}
.photo-drop .icon { color: var(--accent); }
.photo-drop input[type="file"] { max-width: 100%; }

.photo-current {
  position: relative;
  width: 120px; height: 120px; border-radius: var(--radius-md);
  overflow: hidden; border: 1px solid var(--line); margin-bottom: 10px;
}
.photo-current img { width: 100%; height: 100%; object-fit: cover; }

/* Undo picking a file, before the form is submitted - only shown on the
   just-picked preview (see app.js), not on an already-saved photo (that
   one is removed via the "remove_photo" checkbox instead, since it needs
   a real request to take effect). */
.photo-clear {
  position: absolute; top: 4px; right: 4px;
  width: 24px; height: 24px; border-radius: 50%; border: 0; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(30, 26, 22, 0.6); color: #fff; cursor: pointer;
}
.photo-clear:hover { background: rgba(30, 26, 22, 0.8); }

.price-drop-rows { display: flex; flex-direction: column; gap: 10px; }
.price-drop-row {
  display: grid; grid-template-columns: auto 90px auto auto 90px auto;
  align-items: center; gap: 8px;
  font-size: 13px; font-weight: 700; color: var(--ink-soft);
}
.price-drop-row input { padding: 8px 10px; font-size: 14px; }
.price-drop-row .remove {
  border: 0; background: none; cursor: pointer; color: var(--ink-soft);
  padding: 4px; line-height: 0;
}
.price-drop-row .remove:hover { color: var(--warn); }

details.collapsible > summary {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 13px 16px; border: 1px solid var(--line); border-radius: var(--radius-md);
  background: var(--surface); font-size: 14px; font-weight: 700; cursor: pointer;
  list-style: none;
}
details.collapsible > summary::-webkit-details-marker { display: none; }
details.collapsible[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
details.collapsible > .body {
  padding: 16px; border: 1px solid var(--line); border-top: 0;
  border-radius: 0 0 var(--radius-md) var(--radius-md); background: var(--surface);
}

.info-note {
  padding: 12px 14px; border-radius: var(--radius-md);
  background: var(--surface-2); color: var(--ink-soft);
  font-size: 12.5px; line-height: 1.55;
}

/* --------------------------------------------------------------------------
   Rezerwacje i watek wiadomosci
   -------------------------------------------------------------------------- */

.thread-head {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 16px; margin-bottom: 16px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.thread-head .grow { flex-grow: 1; min-width: 0; }
.thread-head .listing-mini { display: flex; align-items: center; gap: 10px; }
.thread-head .listing-mini .thumb {
  width: 44px; height: 44px; border-radius: var(--radius-sm);
  background: var(--surface-2); overflow: hidden; flex-shrink: 0;
}
.thread-head .listing-mini .thumb img { width: 100%; height: 100%; object-fit: cover; }

.messages { display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }

.msg { max-width: 78%; }
.msg .bubble {
  padding: 10px 14px; border-radius: 16px;
  background: var(--surface); border: 1px solid var(--line);
  font-size: 14px; line-height: 1.5; white-space: pre-line;
}
.msg .time { font-size: 11px; color: var(--ink-soft); margin-top: 3px; padding: 0 4px; }
.msg.mine { align-self: flex-end; }
.msg.mine .bubble { background: var(--accent); border-color: var(--accent); color: #fff; }
.msg.mine .time { text-align: right; }
.msg.system {
  align-self: center; max-width: 100%; text-align: center;
}
.msg.system .bubble {
  background: var(--surface-2); border-color: transparent;
  font-size: 12.5px; font-weight: 700; color: var(--ink-soft);
}

.contact-card {
  padding: 16px; margin-bottom: 18px;
  border-radius: var(--radius-lg);
  background: var(--free-bg); border: 1px solid #CFE3CF;
}
.contact-card h3 { color: var(--free); font-size: 14px; margin-bottom: 8px; }
.contact-card dl { display: grid; gap: 6px; margin: 10px 0 0; }
.contact-card .row { display: flex; gap: 8px; font-size: 14px; }
.contact-card dt { font-weight: 700; color: var(--free); min-width: 74px; }
.contact-card dt .icon-inline { margin-right: 3px; }
.contact-card dd { margin: 0; font-weight: 600; }
.contact-methods-list { display: flex; flex-wrap: wrap; gap: 6px; }
.contact-method-tag {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 9px; border-radius: 999px;
  background: #fff; border: 1px solid #CFE3CF;
  font-size: 12.5px; font-weight: 600; color: var(--free);
  text-decoration: none; transition: background .15s, border-color .15s;
}
/* A method with nowhere to link to (see views/reservations/show.php) is a
   plain <a> with no href - inert, so :hover/:focus-visible below never
   apply to it; only the ones that actually open something get the
   "clickable" affordance. */
a.contact-method-tag:hover, a.contact-method-tag:focus-visible {
  background: var(--free-bg); border-color: var(--free);
}

.contact-locked {
  padding: 14px 16px; margin-bottom: 18px;
  border-radius: var(--radius-lg);
  background: var(--surface-2); border: 1px dashed var(--line);
  font-size: 13px; color: var(--ink-soft);
}
.contact-locked h3 { font-size: 13.5px; color: var(--ink); margin-bottom: 4px; }

.composer { display: flex; flex-direction: column; gap: 10px; }
.composer .row { display: flex; gap: 8px; align-items: flex-end; }
.composer textarea { min-height: 64px; }

.reservation-actions {
  display: flex; flex-wrap: wrap; gap: 8px;
  padding-top: 16px; margin-top: 16px; border-top: 1px solid var(--line);
}

.list-rows { display: flex; flex-direction: column; gap: 10px; }
.list-row {
  display: flex; align-items: center; gap: 12px; padding: 12px 14px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-md); color: var(--ink);
}
.list-row:hover { border-color: #DFD0BC; color: var(--ink); }
/* A reservation waiting on a reaction from the viewer (see
   Reservation::actionRequiredCount()) - a subtle accent border/background
   so it's easy to spot in a list without action-count badges. */
.list-row-action { border-color: var(--accent); background: var(--accent-soft); }
.list-row-action:hover { border-color: var(--accent-dark); }
.list-row-action-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent); margin-right: 6px; flex-shrink: 0;
}
.list-row .thumb {
  width: 52px; height: 52px; border-radius: var(--radius-sm); flex-shrink: 0;
  background: var(--surface-2); overflow: hidden;
}
.list-row .thumb img { width: 100%; height: 100%; object-fit: cover; }
.list-row .grow { flex-grow: 1; min-width: 0; }
.list-row .title { font-size: 14px; font-weight: 700; }
.list-row .sub {
  display: block; font-size: 12.5px; color: var(--ink-soft);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.list-row .actions { display: flex; flex-wrap: wrap; gap: 6px; flex-shrink: 0; }
.list-row form { margin: 0; }
/* On a narrow phone, .actions (views/listings/mine.php's Ukryj/Oznacz als
   verkauft/Edytuj buttons) sat in the SAME flex row as .thumb/.grow -
   .actions never shrinks (flex-shrink: 0) and .grow can shrink to
   nothing (min-width: 0), so the title/price text got squeezed into a
   sliver while the buttons themselves still overflowed past the edge of
   the screen, overlapping the thumbnail of the row below. Below 640px
   (views/reservations/index.php and account/reservations.php's own
   .list-row rows have no .actions at all, so this only visibly changes
   anything on the pages that do), .actions moves to its own full-width
   row under thumb+title instead of fighting them for the same line. */
@media (max-width: 639px) {
  .list-row { display: grid; grid-template-columns: 52px 1fr; column-gap: 12px; row-gap: 8px; }
  .list-row .actions { grid-column: 1 / -1; }
}

/* An overly long listing title in a list row (e.g. the reservations
   list) is clamped to 2 lines instead of pushing the row's height
   around or overflowing - the rest is just cut, not wrapped further. */
.reservation-title {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; white-space: normal; line-height: 1.3;
}

/* Przelacznik widoku kupujacy / sprzedajacy (lista rezerwacji) */
.tabs { display: flex; gap: 4px; margin-bottom: 16px; border-bottom: 1px solid var(--line); }
.tab {
  display: inline-flex; align-items: flex-start; gap: 5px;
  padding: 10px 16px; font-size: 14px; font-weight: 700; color: var(--ink-soft);
  border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px;
  background: none; font-family: inherit; cursor: pointer;
}
.tab:hover { color: var(--ink); }
.tab.active { color: var(--accent); border-bottom-color: var(--accent); }
/* Unread-message count (views/reservations/index.php) - sits at the top
   edge of the tab, next to the label, echoing .notif-link .badge's corner
   placement in the header nav without risking overlap on a text tab. */
.tab .badge {
  margin-top: -3px; min-width: 16px; height: 16px; padding: 0 4px; font-size: 10px;
}

/* --------------------------------------------------------------------------
   Konto i profil
   -------------------------------------------------------------------------- */

.profile-head {
  display: flex; flex-wrap: wrap; align-items: center; gap: 16px;
  padding: 20px; margin-bottom: 18px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.profile-head .grow { flex-grow: 1; min-width: 180px; }

.stat-row {
  display: grid; gap: 10px; margin-bottom: 18px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 640px) { .stat-row { grid-template-columns: repeat(5, minmax(0, 1fr)); } }

.stat {
  padding: 14px 16px; text-align: center;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);
}
.stat .val { font-family: 'Nunito', sans-serif; font-size: 22px; font-weight: 800; }
.stat .lbl { font-size: 11.5px; font-weight: 700; color: var(--ink-soft); }

.rating-summary { display: flex; flex-wrap: wrap; align-items: center; gap: 24px; }
.rating-big { text-align: center; flex-shrink: 0; }
.rating-big .val { font-family: 'Nunito', sans-serif; font-size: 42px; font-weight: 800; line-height: 1; }
.rating-bars { flex-grow: 1; min-width: 200px; display: flex; flex-direction: column; gap: 5px; }
.rating-bar { display: flex; align-items: center; gap: 8px; font-size: 12px; font-weight: 700; color: var(--ink-soft); cursor: pointer; border-radius: 6px; padding: 2px 4px; margin: 0 -4px; }
.rating-bar:hover, .rating-bar.active { background: var(--surface-2); color: var(--ink); }
.rating-bar:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.rating-bar .track { flex-grow: 1; height: 7px; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.rating-bar .fill { height: 100%; border-radius: 999px; background: var(--promo); }
.rating-bar .n { min-width: 24px; text-align: right; }

.review { padding: 14px 0; border-bottom: 1px solid var(--line); }
.review:last-child { border-bottom: 0; }
.review-head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.review-head .grow { flex-grow: 1; min-width: 0; }
.review p { margin: 6px 0 0; font-size: 14px; }

/* --------------------------------------------------------------------------
   Uwierzytelnianie
   -------------------------------------------------------------------------- */

.auth-card {
  max-width: 420px; margin: 24px auto;
  padding: 26px 22px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
}
.auth-card h1 { font-size: 22px; text-align: center; }
.auth-card > p.muted { text-align: center; margin-bottom: 20px; }
.auth-foot {
  margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line);
  text-align: center; font-size: 13.5px; color: var(--ink-soft);
}

/* "Sign in with Google/Apple/Facebook" (views/partials/oauth-buttons.php) */
.auth-divider {
  display: flex; align-items: center; gap: 12px;
  margin: 18px 0; color: var(--ink-soft); font-size: 12.5px;
}
.auth-divider::before, .auth-divider::after {
  content: ''; flex: 1 1 auto; height: 1px; background: var(--line);
}
.oauth-buttons { display: flex; flex-direction: column; gap: 10px; }
/* Google's own renderButton() draws its branded SVG button inline here -
   center it since its width is capped in app.js, not full-bleed. */
.oauth-buttons > [data-google-client-id] { display: flex; justify-content: center; }
.oauth-btn-apple { background: #000; color: #fff; border-color: #000; }
.oauth-btn-apple:hover { background: #1a1a1a; }
.oauth-btn-facebook { background: #1877f2; color: #fff; border-color: #1877f2; }
.oauth-btn-facebook:hover { background: #166fe5; }

/* --------------------------------------------------------------------------
   Strony prawne
   -------------------------------------------------------------------------- */

.prose { max-width: 74ch; }
.prose h2 { margin-top: 26px; font-size: 18px; }
.prose h2:first-of-type { margin-top: 0; }
.prose p { color: #4A413A; }
.prose dl { display: grid; gap: 4px; margin: 0 0 12px; }
.prose dt { font-weight: 700; font-size: 13px; color: var(--ink-soft); }
.prose dd { margin: 0 0 8px; }

/* --------------------------------------------------------------------------
   Stopka
   -------------------------------------------------------------------------- */

.site-footer {
  border-top: 1px solid var(--line); background: var(--surface);
  padding: 26px 0; margin-top: 40px;
}
.footer-inner { display: flex; flex-direction: column; gap: 10px; }
.footer-inner p { margin: 0; font-size: 13px; color: var(--ink-soft); }
.footer-links { display: flex; flex-wrap: wrap; gap: 16px; font-size: 13px; font-weight: 700; }
@media (min-width: 720px) {
  .footer-inner { flex-direction: row; align-items: center; justify-content: space-between; }
}

/* ==========================================================================
   Panel administratora
   Osobny, szerszy uklad - administratorzy pracuja glownie na komputerze.
   Boczne menu pojawia sie od 900 px; nizej zwija sie w poziomy pasek.
   ========================================================================== */

body.admin { background: #F7F3EC; }

.admin-shell { display: block; min-height: 100vh; }
@media (min-width: 900px) {
  .admin-shell { display: grid; grid-template-columns: 232px minmax(0, 1fr); }
}

.admin-sidebar {
  background: var(--surface); border-bottom: 1px solid #E7DECB;
  padding: 12px 14px;
}
@media (min-width: 900px) {
  .admin-sidebar {
    border-bottom: 0; border-right: 1px solid #E7DECB;
    padding: 22px 14px; position: sticky; top: 0; height: 100vh;
    display: flex; flex-direction: column; gap: 3px; overflow-y: auto;
  }
}

.admin-logo {
  display: flex; align-items: center; gap: 9px;
  font-family: 'Nunito', sans-serif; font-weight: 800; font-size: 17px;
  color: var(--ink); padding: 0 10px 14px;
}
.admin-logo:hover { color: var(--ink); }
@media (min-width: 900px) { .admin-logo { padding-bottom: 22px; } }

.admin-nav { display: flex; gap: 3px; overflow-x: auto; padding-bottom: 4px; }
@media (min-width: 900px) { .admin-nav { flex-direction: column; overflow: visible; padding: 0; } }

.admin-nav a {
  display: flex; align-items: center; gap: 11px; white-space: nowrap;
  padding: 11px 12px; border-radius: 11px;
  font-size: 13.5px; font-weight: 700; color: var(--ink-soft);
}
.admin-nav a:hover { background: #F0E9DC; color: var(--ink); }
.admin-nav a.active { background: var(--accent-soft); color: var(--accent); }
.admin-nav a .count {
  margin-left: auto; padding: 1px 7px; border-radius: 999px;
  background: var(--warn-bg); color: var(--warn); font-size: 11px; font-weight: 800;
}

.admin-main { display: flex; flex-direction: column; min-width: 0; }

.admin-topbar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 12px; padding: 14px 20px;
  border-bottom: 1px solid #E7DECB; background: var(--surface);
}
.admin-topbar h1 { margin: 0; font-size: 18px; font-weight: 800; }
.admin-topbar .tools { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.admin-topbar form { display: flex; gap: 8px; margin: 0; }
.admin-topbar input[type="search"], .admin-topbar select {
  width: auto; min-width: 180px; padding: 8px 12px; font-size: 13px;
  background: #F0E9DC; border-color: transparent;
}

.admin-content { padding: 20px; }
@media (min-width: 900px) { .admin-content { padding: 24px 28px; } }

.kpi-row {
  display: grid; gap: 14px; margin-bottom: 20px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 900px) { .kpi-row { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; } }

.kpi {
  background: var(--surface); border: 1px solid #E7DECB;
  border-radius: 16px; padding: 16px 18px;
}
.kpi .lbl { font-size: 12px; font-weight: 700; color: var(--ink-soft); margin-bottom: 6px; }
.kpi .val { font-family: 'Nunito', sans-serif; font-size: 26px; font-weight: 800; }
.kpi .val.warn { color: var(--warn); }
.kpi .delta { font-size: 11.5px; color: var(--free); font-weight: 700; margin-top: 3px; }

.panel {
  background: var(--surface); border: 1px solid #E7DECB;
  border-radius: 16px; padding: 18px; margin-bottom: 16px;
}
.panel > h2, .panel > h3 { font-size: 14px; font-weight: 800; margin: 0 0 14px; }
.panel-head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 14px;
}
.panel-head h2, .panel-head h3 { margin: 0; font-size: 14px; font-weight: 800; }

.admin-cols { display: grid; gap: 16px; }
@media (min-width: 1100px) {
  .admin-cols-2 { grid-template-columns: 1.35fr 1fr; }
  .admin-cols-side { grid-template-columns: 340px minmax(0, 1fr); align-items: start; }
}

/* Wykres slupkowy - czysty CSS, bez biblioteki */
.bar-chart { display: flex; align-items: flex-end; gap: 8px; height: 150px; }
.bar-chart .col { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; height: 100%; gap: 6px; }
.bar-chart .bar {
  border-radius: 6px 6px 0 0; min-height: 3px;
  background: linear-gradient(180deg, #F0A182, var(--accent));
}
.bar-chart .lbl { font-size: 10.5px; font-weight: 700; color: var(--ink-soft); text-align: center; }
.bar-chart .val { font-size: 10.5px; font-weight: 800; text-align: center; }
.chart-empty { color: var(--ink-soft); font-size: 13px; padding: 30px 0; text-align: center; }

/* Lista klucz-wartosc (np. panel z informacjami o uzytkowniku) */
.kv-list { display: flex; flex-direction: column; gap: 10px; margin: 0; }
.kv-list .row {
  display: flex; justify-content: space-between; gap: 10px;
  padding-bottom: 10px; border-bottom: 1px solid #F0E9DC; font-size: 13.5px;
}
.kv-list .row:last-child { border-bottom: 0; padding-bottom: 0; }
.kv-list dt { font-weight: 700; color: var(--ink-soft); }
.kv-list dd { margin: 0; font-weight: 700; }

.split-list { display: flex; flex-direction: column; gap: 9px; }
.split-row { display: flex; align-items: center; gap: 10px; font-size: 12.5px; font-weight: 700; }
.split-row .name { min-width: 110px; }
.split-row .track { flex-grow: 1; height: 8px; border-radius: 999px; background: #F0E9DC; overflow: hidden; }
.split-row .fill { height: 100%; border-radius: 999px; background: var(--accent); }
.split-row .pct { min-width: 42px; text-align: right; color: var(--ink-soft); }

/* Tabele admina */
.table-wrap { overflow-x: auto; }
table.admin-table { width: 100%; border-collapse: collapse; font-size: 13px; min-width: 620px; }
table.admin-table th, table.admin-table td {
  padding: 10px 12px; text-align: left; border-bottom: 1px solid #F0E9DC;
  vertical-align: middle;
}
table.admin-table th {
  font-size: 11.5px; font-weight: 800; color: var(--ink-soft);
  text-transform: uppercase; letter-spacing: .03em;
}
table.admin-table tbody tr:last-child td { border-bottom: 0; }
table.admin-table tbody tr:hover { background: #FCFAF6; }
table.admin-table td.actions { white-space: nowrap; }

/* Sortable column headers (admin users/listings tables) - plain link,
   same muted look as the header text itself until active. */
.sort-link {
  display: inline-flex; align-items: center; gap: 3px;
  color: inherit; text-decoration: none;
}
.sort-link:hover, .sort-link.active { color: var(--accent); }
.sort-arrow { transition: transform .15s; }
.sort-arrow-up { transform: rotate(180deg); }
table.admin-table td.actions form { display: inline; margin: 0; }
table.admin-table .cell-main { display: flex; align-items: center; gap: 10px; }
table.admin-table .cell-main .thumb {
  width: 40px; height: 40px; border-radius: 8px; flex-shrink: 0;
  background: #F0E9DC; overflow: hidden;
}
table.admin-table .cell-main .thumb img { width: 100%; height: 100%; object-fit: cover; }

/* Kolejnosc jezykow (admin/settings.php) - reorderowalna lista przez
   przeciaganie, patrz app.js "Kolejnosc jezykow przez przeciaganie". */
.lang-order-list { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.lang-order-row {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: 10px; border: 1px solid transparent;
  background: #FCFAF6; cursor: grab;
}
.lang-order-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.lang-order-row.is-dragging { opacity: .4; }
.lang-order-row.is-drag-over { border-color: var(--accent); }
.lang-order-handle { display: flex; color: var(--ink-soft); flex-shrink: 0; }
.lang-order-rank { width: 20px; text-align: right; font-weight: 700; color: var(--ink-soft); flex-shrink: 0; }
.lang-order-name { flex: 1; }

/* Kategorie */
.cat-list { display: flex; flex-direction: column; gap: 5px; }
.cat-row {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 12px; border-radius: 12px; color: var(--ink);
}
.cat-row:hover { background: #F7F3EC; color: var(--ink); }
.cat-row.selected { background: var(--accent-soft); color: var(--accent); }
.cat-row .caticon {
  width: 34px; height: 34px; border-radius: 9px; flex-shrink: 0;
  background: #F0E9DC; display: flex; align-items: center; justify-content: center;
  color: var(--accent);
}
.cat-row .catname { font-size: 13.5px; font-weight: 700; flex-grow: 1; }
.cat-row .catcount { font-size: 11px; font-weight: 700; color: var(--ink-faint); }
.cat-row.inactive .catname { opacity: .55; text-decoration: line-through; }

.sub-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 11px 12px; border: 1px solid #E7DECB; border-radius: 12px;
  margin-bottom: 8px;
}
.sub-row .subname { font-size: 13px; font-weight: 700; flex-grow: 1; min-width: 140px; }

.size-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.size-tag {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 6px 5px 11px; border-radius: 999px;
  background: #F0E9DC; font-size: 12.5px; font-weight: 700;
}
.size-tag form { margin: 0; display: flex; }
.size-tag button {
  border: 0; background: none; cursor: pointer; color: var(--ink-soft);
  padding: 0 2px; line-height: 0;
}
.size-tag button:hover { color: var(--warn); }
.size-tag.inherited { background: transparent; border: 1px dashed #E7DECB; color: var(--ink-soft); }

.log-view {
  max-height: 62vh; overflow: auto; padding: 14px;
  background: #2A2521; border-radius: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px; line-height: 1.6; color: #E7DECB;
}
.log-view div { white-space: pre-wrap; word-break: break-word; }
.log-view div.err { color: #F0A182; }

/* --------------------------------------------------------------------------
   Drobiazgi
   -------------------------------------------------------------------------- */

.icon-inline { display: inline-block; vertical-align: -2px; }

form { margin: 0 0 0; }
.inline-form { display: inline; }

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
  .listing-card:hover { transform: none; }
}
