/* The register's card and the surfaces made of cards: the catalogue layout
   and the paid strips beside it. Split out of app.css (#324). */

/* ---------- listing cards ---------- */
/* The design sizes the register by the card, not by the breakpoint: a column is
   never narrower than 232px and the count falls out of the space there is. Fixed
   column counts put four 150px cards beside the filter rail, and at that width
   the title, the latin name and the seller row all truncate to nothing. */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
  gap: 18px 14px;
}
/* One card across on a phone: two 170px columns squashed the title, the latin
   name and the badges into an unreadable sliver. */
@media (max-width: 559px) { .grid { grid-template-columns: minmax(0, 1fr); } }

/* The opaque card: an 8px frame around the photo, with the text below it.
   The photo is inset rather than bled to the edge, so the bird sits inside the
   card instead of being the card — a whole register of them reads calmer. */
.card {
  /* The frame colour, neutral by default — every state below sets it and
     nothing else about the card changes, so a register of five states still
     reads as one register. */
  --paid-edge: var(--border-subtle);
  position: relative;
  display: flex;
  flex-direction: column;
  padding: var(--space-2);
  background: var(--surface-card);
  border: 1px solid var(--paid-edge);
  box-shadow: var(--shadow-card);
  border-radius: var(--radius-card);
}
.card:hover { border-color: var(--border-strong); }
.card-link { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.card-photo {
  position: relative;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: var(--surface-lilac);
  border-radius: var(--radius-media);
}
.card-photo img { width: 100%; height: 100%; object-fit: cover; object-position: top; }
.card-body {
  flex: 1;
  display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-4) var(--space-3) var(--space-3);
  min-width: 0;
}
.card-title {
  font-weight: 700;
  font-size: 16.5px;
  line-height: 1.3;
  letter-spacing: -.015em;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.card:hover .card-title { color: var(--action-primary); }
/* Species and place on one line: italics for the Latin name, plain for where
   the bird is. Truncated rather than wrapped — a second line here would push
   the price row out of alignment across the row of cards. */
.card-sub {
  margin-top: 5px;
  font-size: 13.5px; color: var(--text-secondary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The species is set apart by its italic and by being a shade quieter than the
   place beside it, not by a colour: the card says its state in the frame and in
   the pill, and a third colour in the text made every register card look like it
   had bought something. */
.card-sub .latin { font-style: italic; color: var(--text-muted); }
.card .badges { gap: var(--space-1) 6px; }
.card .badge { height: 26px; padding: 0 11px; font-size: 12px; gap: 4px; }
/* Who sells and for how much, in the warm box the design closes the card with:
   the seller and the price are the two things a buyer compares across a row of
   cards, and a tinted block finds them faster than a hairline did.
   margin-top:auto pins the box to the floor, so a two-line title never moves it. */
.card-foot {
  margin-top: auto;
  padding: var(--space-2) 10px;
  background: var(--surface-warm);
  border-radius: var(--radius-thumbnail);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-2);
  min-width: 0;
}
.card-breeder { display: flex; align-items: center; gap: 7px; min-width: 0; }
/* Meadow, the one green in the palette: the breeder's mark is the human half of
   the card and the only place a card is allowed to be green. */
.card-breeder .avatar.sm {
  width: 24px; height: 24px; font-size: 10.5px; flex: none;
  background: var(--surface-meadow); border-color: transparent;
}
.card-breeder-name {
  font-size: 12.5px; font-weight: 500; color: var(--text-secondary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The price is the site's action colour, deepened to the weight a bold 16px
   number needs against the card surface — the lilac at its token strength reads
   lighter than the title above it, and the price must not lose that contest. */
.card-price {
  flex: none;
  font-size: 16px; font-weight: 700;
  letter-spacing: -.02em;
  color: color-mix(in srgb, var(--action-primary) 76%, var(--text-primary));
}
/* A giveaway is not a cheap price, it is a different act — so it does not wear
   the price colour at all (view.go, PriceText). */
.card-price.free { color: var(--state-success); font-size: 14px; }
/* The flag corner. A card may carry two of them at once — wyróżnione above
   pilne — so they stack instead of sharing one absolute position. */
.card-flags {
  position: absolute;
  top: 22px; inset-inline-start: 22px;
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--space-1);
  z-index: 2;
}
.card-flag {
  height: 28px; padding: 0 12px;
  background: var(--surface-frost);
  border: 1px solid var(--border-frost);
  color: var(--text-primary);
  font-size: 11.5px; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase;
  display: inline-flex; align-items: center; gap: var(--space-1);
  border-radius: var(--radius-pill);
}
.card-flag.sold { background: var(--action-primary); color: var(--action-ink); border-color: transparent; }
.card-flag.expired { background: var(--surface-frost); color: var(--text-secondary); }
/* One pastel per state, and the frame below repeats it: lilac for „Wyróżnione",
   coral for „Polecane", butter for „Pilne", meadow for the unbought „Nowe".
   Four services in four colours — two of them sharing one pastel were one
   service to anybody scrolling past. */
.card-flag.featured { background: var(--surface-lilac); color: var(--text-primary); border-color: transparent; }
.card-flag.promoted { background: var(--surface-coral); color: var(--text-primary); border-color: transparent; }
.card-flag.urgent { background: var(--surface-butter); color: var(--text-primary); border-color: transparent; }
.card-flag.is-new { background: var(--surface-meadow); color: var(--text-primary); border-color: transparent; }
.card-flag .ico-crown, .card-flag .lucide { color: currentColor; }

/* The frame. Five states, four frames — each one the pastel of the pill in its
   own corner, so the frame and the pill say the same thing and the card is
   readable from across the register before a word of it is. The frame and the
   pill are the whole graphical-highlight product: neither changes the card's
   position in the ordinary register (ADR 0015 § 1).
   A card can carry two at once, and the cascade settles it in the order the
   flags stack: „Nowe" yields to everything that was paid for, and above it the
   surface a card is standing in outranks what its seller bought. */
.card.is-new, .card.featured, .card.urgent, .card.promoted { border-width: 2px; }
/* Hover lifts a plain card's hairline to --border-strong; a framed card keeps
   the colour of its state. */
.card.is-new:hover, .card.featured:hover, .card.urgent:hover, .card.promoted:hover { border-color: var(--paid-edge); }
.card.is-new { --paid-edge: var(--surface-meadow); }
.card.urgent { --paid-edge: var(--surface-butter); }
.card.featured { --paid-edge: var(--surface-lilac); }
.card.promoted { --paid-edge: var(--surface-coral); }
.card-fav { position: absolute; top: 8px; inset-inline-end: 8px; z-index: 1; }
.fav-btn {
  width: 44px; height: 44px;
  border: 1px solid var(--border-subtle);
  background: color-mix(in srgb, var(--surface-card) 88%, transparent);
  color: var(--text-primary);
  font-size: 14px;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-pill);
}
.fav-btn.on { color: var(--state-danger); }

.pager { display: flex; justify-content: center; }


/* ---------- paid exposure surfaces: „Polecane" strip + category TOP (#111) ---------- */
/* These sections sit beside the register, never inside it, and every one of them
   is built the same way — heading, way out, rail of full cards — so no exposure
   reads as the lesser product. Which listing is paid for is said on the card
   itself, by the pill it wears (art. 12a upk; regulamin § 11 ust. 1).
   The strip is a surface of its own — a warm 2px ring around the register, set
   apart from the register by material rather than by a caption. A buyer who
   reads nothing still sees that this block is not the register.

   The accent is the service standing in it, not a fixed coral: a „Polecane"
   section washed coral around butter-framed cards read as two unrelated
   products on one screen. Each surface names its own below; the default is left
   to „Wyróżnione", the one service with no section of its own. */
/* The ring is a padding band, not a border, so it only stays even if the inner
   curve is the outer one minus the band. Both are derived from --radius-surface
   here and nowhere else: the register used to set the outer radius to hero and
   the inner one to panel, eight pixels apart, and a 2px band drawn between two
   curves that far out of concentric collapses to half a pixel on the diagonal —
   the frame looked cut off at every corner. */
.paid-surface {
  --surface-accent: var(--surface-lilac);
  --accent-wash: 26%;
  --radius-surface: var(--radius-hero);
  --ring: 2px;
  margin-block: var(--space-6);
  padding: var(--ring);
  border-radius: var(--radius-surface);
  background: color-mix(in srgb, var(--surface-accent) 62%, var(--surface-warm));
}
/* Coral for „Polecane" and butter for „Pilne" — the same two tokens
   .card.promoted and .card.urgent frame their cards with, so the section reads
   as the frame's larger sibling. TOP keeps the default.

   The wash is dosed per accent rather than shared, because the two tokens do
   not carry the same chroma: one percentage over both would have made
   „Polecane" shout across the page while „Pilne" barely tinted, and two
   exposures that cost the same have to look like they cost the same. */
#polecane, #polecane-shop { --surface-accent: var(--surface-coral); --accent-wash: 20%; }
#pilne, #pilne-shop { --surface-accent: var(--surface-butter); --accent-wash: 34%; }
.paid-inner {
  padding: clamp(var(--space-5), 3.4vw, var(--space-10));
  border-radius: calc(var(--radius-surface) - var(--ring));
  background: color-mix(in srgb, var(--surface-accent) var(--accent-wash), var(--surface-card));
}
/* Heading and the way out on one line, meeting on the first baseline. */
.paid-head {
  display: flex; align-items: baseline; justify-content: space-between;
  flex-wrap: wrap; gap: var(--space-4) var(--space-8);
  margin-bottom: var(--space-6);
}
.paid-title { display: flex; align-items: center; gap: var(--space-3); }
.paid-title .section-h { margin: 0; }
/* .paid-note is no longer part of this head — the purchase screens (exposure,
   bump, docs) still set the ranking sentence in it, and it hangs on the right
   edge of its block there, ragged-left. */
.paid-note {
  margin: 0 0 0 auto; max-width: 52ch; text-align: right;
  font-size: 14px; color: var(--text-secondary); line-height: 1.5;
}
/* Wrapped onto its own line there is no heading to balance, so it goes back to
   reading order. */
@media (max-width: 719px) { .paid-note { margin-inline-start: 0; text-align: left; } }
/* The same track sizing as the register below, so the strip and the register
   line up column for column instead of reading as two different products. */
.paid-rail { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); }

/* The register's paid blocks wrap all their cards instead of hiding the next
   row. At phone width they stay two-up, so six cards form three complete rows —
   and both blocks take the rule, or one of the twins reads as the wider product
   on a phone. */
.paid-inner { container-type: inline-size; }
@media (max-width: 559px) {
  #polecane-shop .paid-rail, #pilne-shop .paid-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* At that width the box is ~125px and the mark, the name and the price do not
     share a line — the mark sat on the price. Only here: everywhere the row fits
     it stays one line, and a long seller name ellipsizes rather than wraps. */
  .paid-rail .card-foot { flex-wrap: wrap; }
}

.length-set { margin: 0; padding: 0; border: 0; display: grid; gap: 8px; }
.length-set legend { padding: 0 0 6px; font-size: 12px; color: var(--text-secondary); }
.length-row {
  display: flex; align-items: center; gap: 10px; padding: 12px 14px;
  border: 1px solid var(--border-strong); cursor: pointer;
  border-radius: var(--radius-card);
}
.length-row:has(input:checked) { border-color: var(--action-primary); background: color-mix(in srgb, var(--action-primary) 12%, transparent); }
.length-days { flex: 1 1 auto; font-size: 14px; }
.length-price { font-size: 15px; font-weight: 600; }

.slot-box {
  margin-block: 12px; padding: 12px 14px; border: 1px solid var(--border-strong); background: var(--surface-card);
  border-radius: var(--radius-card);
}
.slot-box.full { border-color: var(--action-primary); background: color-mix(in srgb, var(--action-primary) 10%, transparent); }
.slot-box p { margin: 6px 0 0; font-size: 12px; color: var(--text-secondary); line-height: 1.6; }
.slot-count { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; font-size: 14px; font-weight: 600; }
.slot-cat { font-size: 12px; font-weight: 400; color: var(--text-secondary); }

/* The two sources of a publication add up in .slot-count and stay itemised
   here: free monthly ones renew, pack credits expire. */
.allowance-sources { margin: 6px 0 0; padding: 0; list-style: none; display: grid; gap: 2px; }
.allowance-sources li { display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; font-size: 12px; }
.allowance-source { color: var(--text-primary); }
.allowance-when { color: var(--text-secondary); }

.exposure-row {
  display: flex; gap: 12px; align-items: flex-start; padding: 12px 0;
  border-bottom: 1px solid var(--border-subtle);
}
.exposure-mid { flex: 1 1 auto; min-width: 0; display: grid; gap: 6px; justify-items: start; }
.exposure-price { flex: 0 0 auto; font-size: 14px; font-weight: 600; }
.badge.paid { color: var(--action-ink); background: var(--action-primary); border-color: transparent; }

/* The narrowed-empty page: hints under the sentence, in the register's quiet
   voice — the reader is being helped, not told off. */
.empty-hints {
  margin: 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 5px;
  font-size: 13.5px; color: var(--text-muted);
}
