<script>
  let { article, onaction, onreplace, hero = false } = $props();

  // Cards can be replaced in place, so derive from the current article prop and
  // reset the image-failure flag whenever the image URL changes.
  let failed = $state(false);
  $effect(() => {
    void article.image_url;
    failed = false;
  });
  let hasImg = $derived(!!article.image_url && !failed);
  let safeHref = $derived(
    typeof article.url === 'string' && /^https?:\/\//.test(article.url) ? article.url : '#'
  );

  function act(kind, value) {
    if (value) onaction?.(kind, value);
  }
</script>

<!-- Image-less cards are designed, not missing: secondary cards go text-first
     (no empty media band), and the hero becomes a fully typographic lead with a
     faint topic wordmark behind it. We never reserve big empty image space. -->
<article
  class:hero
  class:textfirst={!hero && !hasImg}
  class:herotype={hero && !hasImg}
  data-topic={article.topic ?? ''}
>
  {#if hasImg}
    <a class="media" href={safeHref} target="_blank" rel="noopener">
      <img src={article.image_url} alt="" loading="lazy" referrerpolicy="no-referrer"
           onerror={() => (failed = true)} />
    </a>
  {/if}

  <div class="body">
    <div class="tags">
      {#if article.topic}<span class="tag">{article.topic}</span>{/if}
      {#if article.flavor}<span class="tag soft">{article.flavor}</span>{/if}
      <span class="src">{article.source}</span>
    </div>

    <h3><a href={safeHref} target="_blank" rel="noopener">{article.title}</a></h3>

    {#if article.paywalled}
      <p class="paywall">May need a subscription to read</p>
    {/if}

    {#if hero && article.description}
      <p class="desc">{article.description}</p>
    {/if}

    {#if article.reason_text}
      <p class="why">{article.reason_text}</p>
    {/if}

    <div class="actions">
      {#if onreplace}
        <button class="replace" onclick={() => onreplace(article)}>
          {article.paywalled ? 'Find one I can read' : 'Replace'}
        </button>
      {/if}
      {#if article.topic}<button onclick={() => act('notToday', article.topic)}>Not today</button>{/if}
      {#if article.flavor}<button onclick={() => act('lessLikeThis', article.flavor)}>Less like this</button>{/if}
      {#if article.topic}<button onclick={() => act('alwaysHide', article.topic)}>Hide {article.topic}</button>{/if}
    </div>
  </div>
</article>

<style>
  article {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: var(--shadow);
    display: flex;
    flex-direction: column;
    height: 100%;
  }
  .media {
    position: relative;
    display: block;
    aspect-ratio: 16 / 9;
    background: linear-gradient(135deg, var(--sage-soft), #f1ece0);
  }
  .media img { width: 100%; height: 100%; object-fit: cover; }

  .body { padding: 16px 18px 14px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
  .tags { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 0.74rem; }
  .tag {
    background: var(--sage); color: #fff; border-radius: 999px;
    padding: 2px 9px; font-weight: 600; text-transform: capitalize;
  }
  .tag.soft { background: var(--sage-soft); color: var(--sage-deep); }
  .src { color: var(--muted); margin-left: auto; }

  h3 { font-size: 1.18rem; }
  h3 a:hover { color: var(--sage-deep); }
  .desc { margin: 2px 0 0; color: #3c463a; }
  .paywall {
    margin: 0; font-size: 0.78rem; color: var(--gold);
    display: inline-flex; align-items: center; gap: 5px;
  }
  .paywall::before { content: '🔒'; font-size: 0.72rem; filter: grayscale(0.3); }
  .why {
    margin: 2px 0 0; font-style: italic; color: var(--muted);
    font-size: 0.9rem; padding-left: 12px; border-left: 2px solid var(--sage-soft);
  }
  /* Keep card heights even: clamp variable-length text on lane/grid cards. */
  article:not(.hero):not(.herotype) h3,
  .why {
    display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden;
    -webkit-line-clamp: 3;
  }
  .hero .desc, .herotype .desc {
    display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden;
    -webkit-line-clamp: 6;
  }
  .actions { margin-top: auto; padding-top: 10px; display: flex; gap: 14px; flex-wrap: wrap; }
  .actions button {
    background: none; border: none; padding: 0; color: var(--muted);
    font-size: 0.76rem; border-bottom: 1px dotted var(--line);
  }
  .actions button:hover { color: var(--sage-deep); border-bottom-color: var(--sage); }
  .actions .replace { color: var(--sage-deep); border-bottom-color: var(--sage-soft); }
  /* Tuning actions stay quiet until hover/focus on pointer devices; the Replace
     escape hatch stays visible so a paywalled card always shows a way through. */
  @media (hover: hover) {
    .actions button:not(.replace) { opacity: 0; transition: opacity 0.16s ease; }
    article:hover .actions button:not(.replace),
    article:focus-within .actions button:not(.replace) { opacity: 1; }
  }

  /* text-first secondary card: a small accent instead of an empty image band */
  .textfirst .body { padding-top: 18px; }
  .textfirst .body::before {
    content: ''; display: block; width: 28px; height: 3px;
    background: var(--sage-soft); border-radius: 2px; margin-bottom: 4px;
  }

  /* hero WITH image: two columns */
  .hero { display: grid; grid-template-columns: 1.1fr 1fr; }
  .hero .media { aspect-ratio: auto; height: 100%; min-height: 280px; }
  .hero .body { padding: 28px 30px; justify-content: center; gap: 12px; }
  .hero h3 { font-size: 1.95rem; }

  /* hero WITHOUT image: a fully typographic lead with a faint topic wordmark */
  .herotype {
    position: relative;
    overflow: hidden;
    background:
      radial-gradient(120% 140% at 100% 0%, var(--sage-soft) 0%, transparent 55%),
      linear-gradient(180deg, var(--surface), var(--surface));
  }
  .herotype::after {
    content: attr(data-topic);
    position: absolute;
    right: 8px;
    bottom: -18px;
    font-family: var(--serif);
    font-size: clamp(4rem, 12vw, 8rem);
    line-height: 1;
    color: var(--sage);
    opacity: 0.07;
    text-transform: lowercase;
    letter-spacing: -0.02em;
    pointer-events: none;
    user-select: none;
  }
  .herotype .body { position: relative; padding: 40px 36px; gap: 12px; }
  .herotype h3 { font-size: 2.15rem; }
  .herotype .desc { font-size: 1.05rem; }

  @media (max-width: 640px) {
    .hero { grid-template-columns: 1fr; }
    .hero .media { min-height: 200px; }
    .hero h3, .herotype h3 { font-size: 1.6rem; }
    .herotype .body { padding: 30px 24px; }
  }
</style>
