/** Simple page-number pagination control shared by listing pages. */
function Pagination({ page, totalPages, onChange }) {  if (totalPages <= 1) return null;
  const btn = (active) => ({ width: 32, height: 32, borderRadius: 'var(--radius-sm)', border: active ? 'none' : '1px solid var(--border-default)', background: active ? 'var(--accent)' : 'transparent', color: active ? 'var(--ink-900)' : 'var(--text-secondary)', fontFamily: 'var(--font-mono)', fontSize: 13, cursor: 'pointer' });
  return (
    <div style={{ display: 'flex', justifyContent: 'center', gap: 8, marginTop: 'var(--space-7)' }}>
      <button disabled={page === 1} onClick={() => onChange(page - 1)} style={{ ...btn(false), opacity: page === 1 ? 0.4 : 1 }}>‹</button>
      {Array.from({ length: totalPages }, (_, i) => i + 1).map(n => (
        <button key={n} onClick={() => onChange(n)} style={btn(n === page)}>{n}</button>
      ))}
      <button disabled={page === totalPages} onClick={() => onChange(page + 1)} style={{ ...btn(false), opacity: page === totalPages ? 0.4 : 1 }}>›</button>
    </div>
  );
}

/** Empty-state placeholder for a listing with no results. */
function EmptyState({ message }) {
  const { Icon } = window.MarceloCasteloIODesignSystem_5e00d7;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 10, padding: 'var(--space-9) 0', color: 'var(--text-muted)' }}>
      <Icon name="bookmark" size={28} />
      <div style={{ fontFamily: 'var(--font-body)', fontSize: 15 }}>{message}</div>
    </div>
  );
}
Object.assign(window, { Pagination, EmptyState });

/** Cover thumbnail using the brand's thematic treatment — highlighter-marked keyword(s), faint background shape, and an accent edge. The site default when there's no real photo. */
function CoverThumb({ title, theme = 'var(--accent)', label, highlight = [] }) {
  const renderTitle = () => {
    const words = Array.isArray(highlight) ? highlight : highlight ? [highlight] : [];
    if (!words.length) return title;
    let parts = [title];
    words.forEach((w, hi) => {
      const re = new RegExp('(' + w + ')', 'i');
      parts = parts.flatMap((part, pi) => {
        if (typeof part !== 'string') return [part];
        return part.split(re).map((seg, si) => seg.toLowerCase() === w.toLowerCase()
          ? <mark key={hi + '-' + pi + '-' + si} style={{ background: theme, color: '#0B0F0B', padding: '0 5px', borderRadius: 3, fontWeight: 900, boxDecorationBreak: 'clone', WebkitBoxDecorationBreak: 'clone' }}>{seg}</mark>
          : seg);
      });
    });
    return parts;
  };
  return (
    <div style={{ width: '100%', aspectRatio: '4/3', background: 'var(--ink-950)', position: 'relative', overflow: 'hidden', fontFamily: 'var(--font-display)', containerType: 'size' }}>
      <div style={{ position: 'absolute', top: 0, right: 0, width: 'min(38px, 16cqh)', height: '100%', background: theme, clipPath: 'polygon(100% 0, 100% 100%, 0 100%)' }}></div>
      <div style={{ position: 'absolute', top: 0, left: 0, width: 6, height: '100%', background: theme }}></div>
      <div style={{ position: 'absolute', left: '5cqw', top: '6cqw', right: '5cqw', bottom: '11cqw', display: 'flex', alignItems: 'center' }}>
        <div style={{ color: 'var(--paper)', fontSize: 'clamp(12px, 7cqw, 26px)', fontWeight: 800, lineHeight: 1.2, wordBreak: 'normal', overflowWrap: 'normal', display: '-webkit-box', WebkitLineClamp: 5, WebkitBoxOrient: 'vertical', overflow: 'hidden', textOverflow: 'ellipsis' }}>{renderTitle()}</div>
      </div>
      {label && <div style={{ position: 'absolute', left: '5cqw', bottom: '4cqw', fontFamily: 'var(--font-mono)', fontSize: 'clamp(8px, 2.6cqw, 12px)', fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: theme, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: 'calc(100% - 60px)' }}>{label}</div>}
    </div>
  );
}
Object.assign(window, { CoverThumb });
