/* ==========================================================================
   Blog Filter
   Styles for the AJAX blog listing on page-blog.php: skeleton loaders, card
   entrance animation, the secondary filter row, Load More / infinite scroll
   controls, and the empty and error states.

   Kept out of main.css so it ships only on the one template that uses it,
   matching how the theme already scopes about.css, portfolio.css and the
   rest. Colours are the same literal tokens the existing .blog-list-*
   rules use (#00C2FF accent, #0f172a ink, #64748b muted, #e2e8f0 /
   #f1f5f9 surfaces) so nothing here introduces a second palette.
   ========================================================================== */

/* ---- Screen-reader utility ----
   Guarded with :where() so it has zero specificity and cannot win against
   the theme's own definition if one already exists. */
:where(.screen-reader-text) {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Visible again on focus, so a skipped-to control is not invisible. */
:where(.screen-reader-text):focus-visible {
  position: static;
  width: auto;
  height: auto;
  margin: 0;
  overflow: visible;
  clip: auto;
  white-space: normal;
}

/* ---- Secondary filter row ---- */

.blog-list-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 1rem;
  padding: 1.25rem 0 0;
}

.blog-list-filters__field {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  min-width: 0;
  flex: 1 1 10rem;
}

@media (min-width: 640px) {
  .blog-list-filters__field {
    flex: 0 1 12rem;
  }
}

.blog-list-filters__label {
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #94a3b8;
}

.blog-list-filters__select {
  width: 100%;
  padding: 0.5rem 2rem 0.5rem 0.75rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: #0f172a;
  background-color: #ffffff;
  border: 1px solid #e2e8f0;
  border-radius: 0.625rem;
  cursor: pointer;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;

  /* Native select arrows differ wildly between platforms; an inline SVG
     keeps the control looking the same everywhere. Encoded as a data URI
     rather than a file so this stylesheet stays a single request. */
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.625rem center;
}

.blog-list-filters__select:hover {
  border-color: #cbd5e1;
}

.blog-list-filters__select:focus-visible {
  outline: none;
  border-color: #00C2FF;
  box-shadow: 0 0 0 3px rgba(0, 194, 255, 0.2);
}

.blog-list-filters__reset,
.blog-list-filters__apply {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.5rem 0.875rem;
  font-size: 0.8125rem;
  font-weight: 700;
  color: #64748b;
  text-decoration: none;
  background-color: transparent;
  border: 1px solid #e2e8f0;
  border-radius: 0.625rem;
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}

.blog-list-filters__reset:hover,
.blog-list-filters__apply:hover {
  color: #0f172a;
  border-color: #cbd5e1;
  background-color: #f8fafc;
}

.blog-list-filters__reset[hidden] {
  display: none;
}

.blog-list-filters__reset svg {
  width: 0.875rem;
  height: 0.875rem;
}

/* ---- Focus states ----
   :focus-visible only, so a mouse click never paints a ring, but every
   keyboard path does. */

.blog-list-tabs__link:focus-visible,
.blog-list-card__title a:focus-visible,
.blog-list-card__cta:focus-visible,
.blog-list-empty__reset:focus-visible,
.blog-list-error__retry:focus-visible,
.blog-load-more__button:focus-visible,
.blog-list-pagination a:focus-visible {
  outline: 2px solid #00C2FF;
  outline-offset: 3px;
  border-radius: 0.25rem;
}

.blog-list-search__input:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(0, 194, 255, 0.2);
}

/* The results container is focused programmatically after a page change;
   it should accept focus without painting a ring around the whole block. */
#bc-blog-results:focus {
  outline: none;
}

/* ---- Card entrance ----
   `backwards` fill applies the from-state during the stagger delay, so a
   card is never briefly visible before its own animation starts. Crucially
   there is no `forwards`: once the animation ends the element returns to
   its natural state, leaving no residual inline-equivalent transform to
   block .blog-list-card:hover's translateY. */

@keyframes bc-card-enter {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
}

.bc-card-enter {
  animation: bc-card-enter 220ms cubic-bezier(0.22, 0.61, 0.36, 1) backwards;
  animation-delay: calc(var(--bc-stagger, 0) * 40ms);
}

/* ---- Skeletons ---- */

/* display: contents lets the appended batch wrapper vanish from layout, so
   its skeleton children become grid items of .blog-list-grid directly —
   while still being removable in one operation. */
.blog-skeleton-batch {
  display: contents;
}

.blog-skeleton {
  /* Override the real card's hover lift and shadow: a placeholder that
     responds to the pointer invites a click on nothing. */
  pointer-events: none;
  box-shadow: none;
}

.blog-skeleton:hover {
  transform: none;
  box-shadow: none;
}

.blog-skeleton__media {
  /* Matches .blog-list-card__media's 12rem exactly, which is what keeps the
     swap from shifting layout. */
  height: 12rem;
  flex: 0 0 auto;
}

.blog-skeleton__line {
  height: 0.75rem;
  border-radius: 0.375rem;
}

.blog-skeleton__line--title {
  height: 1rem;
  margin-bottom: 0.5rem;
}

.blog-skeleton__line--meta {
  height: 0.625rem;
  margin-bottom: 0.375rem;
}

.blog-skeleton__line--short {
  width: 60%;
}

.blog-skeleton__excerpt {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 1rem 0 1.5rem;
}

.blog-skeleton__footer {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin-top: auto;
  padding-top: 1rem;
}

.blog-skeleton__avatar {
  width: 28px;
  height: 28px;
  border-radius: 9999px;
  flex: 0 0 auto;
}

.blog-skeleton__footer-text {
  flex: 1 1 auto;
  min-width: 0;
}

/* The shimmer is a single background-position animation on a gradient —
   no extra elements, no box-shadow animation, and it composites cheaply
   even with a dozen placeholders on screen at once. */
.blog-skeleton__shimmer {
  background-color: #f1f5f9;
  background-image: linear-gradient(
    90deg,
    rgba(241, 245, 249, 0) 0%,
    rgba(255, 255, 255, 0.85) 50%,
    rgba(241, 245, 249, 0) 100%
  );
  background-size: 200% 100%;
  background-repeat: no-repeat;
  animation: bc-shimmer 1.4s ease-in-out infinite;
}

@keyframes bc-shimmer {
  from {
    background-position: -100% 0;
  }
  to {
    background-position: 200% 0;
  }
}

/* ---- Load More / infinite scroll ---- */

.blog-load-more {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  padding: 2.5rem 0 0;
}

.blog-load-more__button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 2.75rem;
  padding: 0.75rem 1.75rem;
  font-size: 0.875rem;
  font-weight: 700;
  color: #0f172a;
  text-decoration: none;
  background-color: #ffffff;
  border: 1px solid #e2e8f0;
  border-radius: 9999px;
  cursor: pointer;
  transition: color 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
}

.blog-load-more__button:hover {
  color: #00C2FF;
  border-color: #00C2FF;
  box-shadow: 0 8px 20px -8px rgba(0, 194, 255, 0.5);
  transform: translateY(-2px);
}

.blog-load-more__remaining {
  font-size: 0.8125rem;
  color: #94a3b8;
}

/* Present only so the IntersectionObserver has something to watch; it must
   occupy a sliver of space or it can never intersect. */
.blog-load-more__sentinel {
  width: 100%;
  height: 1px;
}

/* The spinner only exists while a request is in flight. aria-busy on the
   grid is the authoritative signal; this is its visual counterpart. */
.blog-load-more__spinner {
  display: none;
  width: 1rem;
  height: 1rem;
  border: 2px solid rgba(0, 194, 255, 0.25);
  border-top-color: #00C2FF;
  border-radius: 9999px;
  animation: bc-spin 0.7s linear infinite;
}

[aria-busy='true'] ~ [data-bc-pagination] .blog-load-more__spinner {
  display: inline-block;
}

@keyframes bc-spin {
  to {
    transform: rotate(360deg);
  }
}

/* ---- Empty state ---- */

.blog-list-empty {
  /* The empty state is injected into the grid in place of the cards, so it
     has to span every column rather than sit in the first one. */
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.625rem;
  padding: 3.5rem 1.5rem;
  text-align: center;
  background-color: #f8fafc;
  border: 1px dashed #e2e8f0;
  border-radius: 1rem;
}

.blog-list-empty__icon,
.blog-list-error__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: 9999px;
  background-color: rgba(0, 194, 255, 0.1);
  color: #00C2FF;
}

.blog-list-empty__icon svg,
.blog-list-error__icon svg {
  width: 1.5rem;
  height: 1.5rem;
}

.blog-list-empty__title,
.blog-list-error__title {
  font-size: 1.125rem;
  font-weight: 700;
  color: #0f172a;
}

.blog-list-empty__text,
.blog-list-error__text {
  max-width: 32rem;
  font-size: 0.875rem;
  line-height: 1.625;
  color: #64748b;
}

.blog-list-empty__reset,
.blog-list-error__retry {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.75rem;
  margin-top: 0.5rem;
  padding: 0.625rem 1.25rem;
  font-size: 0.875rem;
  font-weight: 700;
  color: #ffffff;
  text-decoration: none;
  background-color: #00C2FF;
  border: 0;
  border-radius: 9999px;
  cursor: pointer;
  transition: background-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
}

.blog-list-empty__reset:hover,
.blog-list-error__retry:hover {
  background-color: #00a8e0;
  box-shadow: 0 8px 20px -8px rgba(0, 194, 255, 0.7);
  transform: translateY(-2px);
}

.blog-list-empty__reset svg,
.blog-list-error__retry svg {
  width: 1rem;
  height: 1rem;
}

/* ---- Error state ---- */

.blog-list-error {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.625rem;
  padding: 3rem 1.5rem;
  text-align: center;
  background-color: #fff7f7;
  border: 1px solid #fee2e2;
  border-radius: 1rem;
}

.blog-list-error__icon {
  background-color: rgba(239, 68, 68, 0.1);
  color: #ef4444;
}

/* ---- Touch targets ----
   Coarse pointers get a 44px minimum on every interactive control, which is
   the smallest reliably tappable size. */

@media (pointer: coarse) {
  .blog-list-tabs__link {
    min-height: 2.75rem;
    display: inline-flex;
    align-items: center;
  }

  .blog-list-filters__select,
  .blog-list-filters__reset {
    min-height: 2.75rem;
  }

  .blog-list-pagination a,
  .blog-list-pagination span {
    min-width: 2.75rem;
    min-height: 2.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
}

/* ---- Reduced motion ----
   Everything animated above is decorative. Someone who has asked the system
   for less motion — often because motion makes them ill — gets the same
   information with none of it. The shimmer is stilled rather than hidden,
   so the loading state is still legible as a loading state. */

@media (prefers-reduced-motion: reduce) {
  .bc-card-enter {
    animation: none;
  }

  .blog-skeleton__shimmer {
    animation: none;
    background-image: none;
  }

  .blog-load-more__spinner {
    animation: none;
  }

  .blog-load-more__button:hover,
  .blog-list-empty__reset:hover,
  .blog-list-error__retry:hover {
    transform: none;
  }
}
