/* ==========================================================================
   Filter pill row — reusable category/topic filter shared by every listing
   page (Portfolio archive, Shop, Blog). One inactive glass style (echoes
   the pitch section's chip glass) plus one active style (brand gradient
   fill) so a visitor always sees which filter is live at a glance.
   ========================================================================== */

.filter-pill-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

/* The resting-state "border" is an inset box-shadow, not a real border —
   see the note on .filter-pill.is-active below for why. Written as 3
   layers (two zero-spread placeholders + the actual ring) so it matches
   .is-active's own 3-layer box-shadow exactly: toggling the class then
   cross-fades every layer in place instead of snapping, since a CSS
   transition can only interpolate box-shadow smoothly when both states
   list the same number of shadows in the same order. */
.filter-pill {
  -webkit-appearance: none;
  appearance: none;
  display: inline-flex;
  align-items: center;
  height: 40px;
  padding: 0 18px;
  border-radius: var(--radius-sm);
  border: none;
  box-shadow: 0 0 0 0 transparent, inset 0 0 0 0 transparent, inset 0 0 0 1px var(--color-border);
  background: var(--color-bg-elevated);
  color: var(--color-text-muted);
  font-size: var(--fs-small);
  font-weight: 600;
  white-space: nowrap;
  transition: background var(--duration-fast) var(--ease-standard),
    box-shadow var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard),
    transform var(--duration-fast) var(--ease-standard);
}

.filter-pill:hover {
  background: var(--color-bg-elevated-hover);
  color: var(--color-text);
}

/* Explicit override instead of relying on the browser's own default focus
   ring — on some OS/browser combos that default ring renders in the
   system accent color (can read as a stray red/pink edge) instead of
   this site's own focus color from base.css. */
.filter-pill:focus {
  outline: none;
}

.filter-pill:focus-visible {
  outline: 2px solid var(--color-orange);
  outline-offset: 2px;
}

/* Same recipe as .btn--cta (header.css) — no real border at all, rather
   than a transparent one, since a same-width transparent border sitting
   on top of a gradient background is what rendered as a stray edge stripe
   on some browsers/zoom levels; .btn--cta never had a border to begin with
   and never showed the issue. The resting ring above is a box-shadow for
   the same reason on the other side of the toggle: swapping a real
   `border: 1px solid` for `border: none` changes border-style, which
   can't transition, so the ring would pop back in abruptly the instant a
   pill deactivates. Its 3rd shadow layer fades that ring's color to
   transparent instead — smooth either direction, no border property
   involved. No hover-lift here — "active" is a resting state, not
   something reacting to the pointer. */
.filter-pill.is-active {
  background: var(--gradient-brand-btn);
  border: none;
  color: #fff;
  box-shadow: var(--shadow-brand), inset 0 1px 0 rgba(255, 255, 255, 0.16), inset 0 0 0 1px transparent;
}

[data-filter-item][hidden] {
  display: none;
}

/* Mobile: no entrance/paging animation on listing grids (Portfolio, Shop,
   Blog) — cards appear instantly, matching the no-ambient-motion rule for
   phones. Overrides the staggered fade/rise below at ≤767px. */
@media (max-width: 767px) {
  [data-filter-item],
  [data-filter-item].is-listing-hidden,
  [data-filter-item].is-listing-leaving {
    transition: none !important;
    transition-delay: 0ms !important;
    opacity: 1 !important;
    transform: none !important;
  }
  /* Clip the grid horizontally so the swipe/drag slide (js/listing.js moves
     the whole grid via translateX) never spills into a horizontal scroll. */
  [data-listing] {
    overflow-x: clip;
  }
}

/* Listing cards own their entrance/exit animation entirely (js/listing.js)
   rather than sharing [data-reveal] — reveal.js's observer only ever fires
   once per element and can't replay when a filter swaps which cards are
   visible. Entrance matches the homepage's own [data-reveal] cadence
   (1100ms/--ease-standard, css/base.css) so scroll-in and filter-in read
   as the same speed; the exit fade is deliberately quicker (300ms) so
   clearing the old set doesn't feel like it's dragging. This same cycle
   plays on EVERY render — switching filter and paging within the same
   filter both fade every visible card out together at once
   (.is-listing-leaving — opacity only, no movement, transition-delay
   reset to 0 by JS so a card that was mid-stagger-in doesn't inherit a
   leftover delay); once that finishes, the new page's cards are released
   one after another in reading order (transition-delay set per card by
   JS, 10 frames/~166ms apart) from .is-listing-hidden (faded + shifted
   down), so they rise into place left-to-right, top-to-bottom instead of
   all snapping in together. */
[data-filter-item] {
  transition: opacity 1100ms var(--ease-standard), transform 1100ms var(--ease-standard);
  transition-delay: var(--listing-delay, 0ms);
}

[data-filter-item].is-listing-hidden {
  opacity: 0;
  transform: translateY(24px);
}

[data-filter-item].is-listing-leaving {
  transition-duration: 300ms;
  opacity: 0;
}

/* --- Pagination: numbered page controls shared by every listing page
   (Portfolio archive, Shop, Blog), rendered by js/listing.js. Its own
   fade (.is-listing-leaving, below) only plays when the number of pages
   actually changes — the button set itself is different, so it has to be
   rebuilt. Paging within the same filter, or switching to a filter with
   the SAME page count, reuses the exact same buttons and just moves which
   one carries .is-active (js/listing.js's updatePaginationButtons) — the
   buttons' own transition below handles that smoothly with no nav-wide
   fade, and if the active button doesn't actually change, nothing
   animates at all. */

.pagination {
  margin-top: var(--space-12);
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  transition: opacity 300ms var(--ease-standard);
}

.pagination.is-listing-leaving {
  opacity: 0;
}

/* Same box-shadow-instead-of-border recipe as .filter-pill (see its own
   comment above) — same reason: .is-active drops the border entirely to
   avoid a gradient-edge seam, and border-style can't transition, so a
   real border toggling back on when a button deactivates would pop in
   abruptly instead of fading. */
.pagination__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  height: 40px;
  padding: 0 14px;
  border-radius: var(--radius-sm);
  border: none;
  box-shadow: 0 0 0 0 transparent, inset 0 0 0 0 transparent, inset 0 0 0 1px var(--color-border);
  background: var(--color-bg-elevated);
  color: var(--color-text-muted);
  font-size: var(--fs-small);
  font-weight: 600;
  transition: background var(--duration-fast) var(--ease-standard),
    box-shadow var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard);
}

/* :not(.is-active) keeps this from outranking .pagination__btn.is-active
   below on specificity (:hover + :not(:disabled) is two pseudo-classes,
   one more than .is-active's single class) — without it, clicking a page
   button while the cursor sits on top would show the hover grey instead
   of the gradient until the pointer moved away. */
.pagination__btn:hover:not(:disabled):not(.is-active) {
  background: var(--color-bg-elevated-hover);
  color: var(--color-text);
}

.pagination__btn:focus {
  outline: none;
}

.pagination__btn:focus-visible {
  outline: 2px solid var(--color-orange);
  outline-offset: 2px;
}

/* Same recipe as .btn--cta / .filter-pill.is-active — see the note there. */
.pagination__btn.is-active {
  background: var(--gradient-brand-btn);
  border: none;
  color: #fff;
  box-shadow: var(--shadow-brand), inset 0 1px 0 rgba(255, 255, 255, 0.16), inset 0 0 0 1px transparent;
}

.pagination__btn:disabled {
  opacity: 0.4;
  cursor: default;
}

/* Gap-filler for a windowed page list (js/listing.js's getPaginationWindow)
   — a plain "…", same footprint as a number button so the row's rhythm
   stays even, but not interactive (it doesn't stand for any single page). */
.pagination__ellipsis {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  height: 40px;
  color: var(--color-text-faint);
  font-size: var(--fs-small);
  font-weight: 600;
  user-select: none;
}

/* Mobile: pagination always on one line — compact buttons, no wrap. Paired with
 * the compact window in js/listing.js (Prev  1 … current … last  Next). */
@media (max-width: 767px) {
	.pagination { flex-wrap: nowrap; gap: 6px; }
	.pagination__btn { min-width: 32px; height: 36px; padding: 0 8px; font-size: 13px; }
	.pagination__ellipsis { min-width: 10px; }
}
