/**
 * Component classes — shared primitives for this site.
 * Agent-authored via set-site-assets. Markup recipes live in components/*.md.
 */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  padding: 0.85rem 1.35rem;
  border-radius: var(--radius-full);
  font-family: var(--font-text);
  font-size: var(--text-sm-size);
  font-weight: var(--weight-semibold);
  line-height: 1.2;
  text-decoration: none;
  border: var(--border-width) solid transparent;
  cursor: pointer;
  transition: transform 0.2s ease, background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.btn:hover {
  transform: translateY(-1px);
}

.btn--primary {
  background: var(--color-action);
  color: var(--color-on-action);
}

.btn--primary:hover {
  background: var(--color-action-hover);
  color: var(--color-on-action);
}

.btn--ghost {
  background: var(--color-surface-subtle);
  color: var(--color-ink);
  border-color: var(--color-border);
}

.btn--ghost:hover {
  border-color: var(--color-ink-muted);
  color: var(--color-ink);
}

.btn--secondary {
  background: transparent;
  color: var(--color-ink);
  border-color: var(--color-border);
}

.btn--secondary:hover {
  border-color: var(--color-ink-muted);
}

.btn__icon { width: 1.125rem; height: 1.125rem; }

.stack {
  display: flex;
  flex-direction: column;
}

.stack > * + * {
  margin-top: var(--space-md);
}

.stack--sm > * + * {
  margin-top: var(--space-sm);
}

.stack--lg > * + * {
  margin-top: var(--space-lg);
}

.stack--xl > * + * {
  margin-top: var(--space-xl);
}

.prose {
  max-width: var(--layout-content);
  font-size: var(--text-md-size);
  line-height: var(--text-md-leading);
  letter-spacing: var(--text-md-tracking);
  color: var(--color-ink);
}

.prose h2,
.prose h3 {
  font-family: var(--font-title);
  font-weight: var(--weight-medium);
  margin: var(--space-xl) 0 var(--space-sm);
}

.prose h2 {
  font-size: var(--title-lg-size);
  line-height: var(--title-lg-leading);
  letter-spacing: var(--title-lg-tracking);
}

.prose h3 {
  font-size: var(--title-md-size);
  line-height: var(--title-md-leading);
  letter-spacing: var(--title-md-tracking);
}

.prose p {
  margin: 0 0 var(--space-md);
  color: var(--color-ink-muted);
}

.prose a {
  color: var(--color-action);
}

/* ---------- post grid ----------
   Card grid for any {{#posts:post-card}} / {{#query:...:post-card}} loop.
   Shared by the News archive and the search results view, so the two cannot
   drift apart. The empty message is a grid child rather than a sibling: the
   loop renders nothing at all when there are no posts, and a container holding
   only whitespace does not match :empty. */
.postgrid {
  display: grid;
  gap: var(--space-xl);
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
}
.postgrid__empty {
  color: var(--color-ink-muted);
}
.postgrid:has(.postcard) .postgrid__empty {
  display: none;
}

/* ---------- pager ---------- */
.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
  margin-top: var(--space-3xl);
  font-family: var(--font-mono);
  font-size: var(--text-xs-size);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}
.pager__pages {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}
.pager a {
  color: var(--color-ink-muted);
  padding: var(--space-xs) var(--space-md);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-full);
  transition: border-color 0.2s ease, color 0.2s ease;
}
.pager a:hover {
  color: var(--color-ink);
  border-color: var(--color-action);
}
.pager a:focus-visible {
  outline: 2px solid var(--color-action);
  outline-offset: 2px;
}
/* Delta emits an empty href for a step that does not exist. */
.pager a[href=""] { display: none; }
.pager__page[aria-current="1"] {
  color: var(--color-on-action);
  background: var(--color-action);
  border-color: var(--color-action);
}

/* ---------- site search field ----------
   Used by the search results view and the 404 page. */
.sitesearch {
  display: flex;
  gap: var(--space-sm);
  flex-wrap: wrap;
  width: min(32rem, 100%);
}
.sitesearch__input {
  flex: 1 1 14rem;
  min-width: 0;
  padding: 0.85rem 1.15rem;
  font-family: var(--font-text);
  font-size: var(--text-md-size);
  line-height: 1.2;
  color: var(--color-ink);
  background: var(--color-surface-body);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-full);
  transition: border-color 0.15s ease;
}
.sitesearch__input::placeholder { color: var(--color-ink-muted); }
.sitesearch__input:focus {
  outline: none;
  border-color: var(--color-action);
}
.sitesearch__submit {
  padding: 0.85rem 1.35rem;
  border-radius: var(--radius-full);
  font-family: var(--font-text);
  font-size: var(--text-sm-size);
  font-weight: var(--weight-semibold);
  line-height: 1.2;
  background: var(--color-action);
  color: var(--color-on-action);
  border: var(--border-width) solid transparent;
  cursor: pointer;
  transition: background 0.15s ease;
}
.sitesearch__submit:hover { background: var(--color-action-hover); }
.sitesearch__submit:focus-visible {
  outline: 2px solid var(--color-action);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .btn, .pager a, .sitesearch__input, .sitesearch__submit { transition: none; }
  .btn:hover { transform: none; }
}

/* ---------- release groups ----------
   Labelled panel for one class of change in a release: new features,
   improvements, fixes. Shared by the Release Card loop item (changelog archive)
   and the Release Single template — a single release page does not render the
   loop item, so its CSS never loads there, and these rules lived in both files
   until they were promoted here.

   Label tones come from the feedback tokens: additions read positive,
   improvements accent, fixes cautionary. No new colours. */
.release-group {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-lg);
  padding: var(--space-2xl);
  background: var(--color-surface-subtle);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-lg);
}

/* A release rarely has all three kinds of change. The fields are HTML, so an
   unfilled one renders as nothing at all and the panel would still draw its
   frame — hence hiding on an empty body rather than on :empty. */
.release-group:not(:has(.release-group__body *)) { display: none; }

.release-group__label {
  font-family: var(--font-text);
  font-size: var(--text-sm-size);
  font-weight: var(--weight-semibold);
  line-height: 1.2;
  border-radius: var(--radius-full);
  padding: 0.5em 1.1em;
}
.release-group--new .release-group__label { color: var(--color-success); background: var(--color-success-bg); }
.release-group--improvement .release-group__label { color: var(--color-action); background: var(--action-soft); }
.release-group--fix .release-group__label { color: var(--color-warning); background: var(--color-warning-bg); }

.release-group__body { color: var(--color-ink-muted); max-width: var(--layout-content); }
.release-group__body ul { display: flex; flex-direction: column; gap: var(--space-sm); padding-left: var(--space-lg); list-style: disc; }
.release-group__body li { line-height: var(--text-md-leading); }
.release-group__body p { margin: 0 0 var(--space-md); }
.release-group__body p:last-child { margin-bottom: 0; }
.release-group__body a { color: var(--color-action); }
