/* Just Mercy · cards
   Split out of the single components.css so concurrent work on different
   areas cannot collide in one file, and so a page can eventually load only
   the CSS its elements need. Tokens come from tokens.css; no literal brand
   hex belongs in here (the page-quality gate's D2 criterion enforces it).

   Depends on core.css for .jm-tag and .jm-link-arrow — the card reuses those
   two atoms as drawn rather than restating their rules here. */

/* =========================================================================
   JM Card — Figma "Molecules / Card / Content" (437:636)

   No spec panel exists for this component; the written guidance is its Figma
   component description, quoted in full in class-jm-card.php.

   The card's own interaction is the one thing Figma does not settle — it draws
   no hover or focus variant for cards, unlike Button, which has both. So the
   behaviour here is the site-wide rule from the Button spec panel applied to a
   card: transitions var(--motion-fast) var(--motion-ease-out), a 3px Purple/500 focus ring at 2px offset
   that is never removed, and no transform under prefers-reduced-motion.
   ========================================================================= */

.jm-card {
  position: relative;                  /* anchor for the stretched title link */
  display: flex;
  flex-direction: column;
  height: 100%;                        /* fill the grid cell — Figma's stretched
                                          instances are 435 and 471 tall against
                                          a 393 master */
  background-color: var(--stone-100);
}

/* Figma clips the whole card; the only thing that needs clipping is the cover
   image, so the clip lives there instead. Clipping the root would also crop the
   focus ring, which sits 2px outside the card's edge. */

/* ---- Cover ---------------------------------------------------------------
   176 tall, padding 20/24, contents bottom-aligned. The band and the image
   occupy the same slot: Figma's "Cover" frame and its hidden "Thumb" rectangle
   are both 411 x 176 and the description says to swap one for the other. */

.jm-card__cover {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;     /* the band title sits on the bottom edge */
  padding: 1.25rem 1.5rem;       /* 20 / 24 */
  overflow: clip;
}

.jm-card__cover--band {
  min-height: 11rem;             /* 176 — min, not fixed: Figma clips a long
                                    band title, and growing the band reads
                                    better than beheading the words */
  background-color: var(--purple-500);
}

.jm-card__cover--image {
  height: 11rem;                 /* 176 — definite, so object-fit has something
                                    to fit against */
  padding: 0;                    /* the image is the band; no inset */
}

.jm-card__cover-title {
  margin: 0;
  font: 700 var(--text-heading-s)/1.2 var(--font-sans);   /* Heading/S 21 Bold */
  color: var(--stone-100);
}

.jm-card__image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---- Content -------------------------------------------------------------
   space-between is what anchors the footer to the bottom of a stretched card,
   which is exactly what the Figma description asks for ("in stretched instances
   set Content to Fill vertical"). */

.jm-card__content {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  row-gap: 1.5rem;               /* 24 — the minimum, see the note below */
  padding: 1.5rem;               /* 24 */
}

/* A FLOOR under the Body-to-Footer distance, not a fixed gap.

   The Figma master has them flush (Body ends at y=145, Footer starts at y=145)
   because that card's copy happens to fill the height — `space-between` is doing
   the separating, and on a card whose excerpt runs long there is nothing left to
   distribute, so "Read the article" ends up hard against the last line of the
   excerpt. On The Facts' longest card the gap measured zero.

   `row-gap` on a space-between column is a minimum, not an addition: it only
   applies once the free space is used up. Short cards still sit exactly as drawn;
   long ones stop colliding. */

.jm-card__body,
.jm-card__footer {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.75rem;                  /* 12 */
}

.jm-card__title {
  margin: 0;
  font: 600 var(--text-body-m)/1 var(--font-sans);  /* Body/M SemiBold 18,
                                                       line-height exactly 1 */
  color: var(--purple-500);
}

.jm-card__excerpt {
  margin: 0;
  font: 400 var(--text-body-s)/1.2 var(--font-sans);   /* Body/XS 15 */
  color: var(--purple-400);
}

.jm-card__meta {
  margin: 0;
  font: 400 var(--text-label-s)/1.3 var(--font-sans);  /* Label/S 13. Figma's
                                                          text node is 17 tall at
                                                          13px, i.e. the font's
                                                          own auto line-height */
  color: var(--purple-400);
}

/* ---- The link ------------------------------------------------------------
   The title is the anchor and it stretches over the card, so the target is the
   whole card — far past the 44px minimum — and there is exactly one tab stop,
   named with the article title. See the header comment in class-jm-card.php. */

.jm-card__link {
  color: inherit;
  text-decoration: none;
}

.jm-card__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* Anything else interactive inside a card has to be lifted above that overlay,
   or the overlay swallows it. */
.jm-card a:not(.jm-card__link),
.jm-card button {
  position: relative;
  z-index: 1;
}

.jm-card:hover .jm-card__link {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* The arrow row is decorative (aria-hidden) and belongs to the card, not to
   itself, so it answers to the card's hover rather than its own. */
.jm-card:hover .jm-link-arrow__arrow {
  transform: translateX(0.25em);
}

/* Focus ring: the site-wide 3px Purple/500 at 2px offset. The ring belongs on
   the card, not on the small inline anchor box, so the user can see what they
   are about to activate. */
.jm-card__link:focus-visible {
  outline: 3px solid var(--purple-500);
  outline-offset: 2px;
}

@supports selector(:has(*)) {
  .jm-card__link:focus-visible {
    outline: none;
  }
  .jm-card:has(.jm-card__link:focus-visible) {
    outline: 3px solid var(--purple-500);
    outline-offset: 2px;
  }
  .jm-card:has(.jm-card__link:focus-visible) .jm-card__link {
    text-decoration: underline;
    text-underline-offset: 0.15em;
  }
}

@media (prefers-reduced-motion: reduce) {
  .jm-card:hover .jm-link-arrow__arrow {
    transform: none;
  }
}


/* =========================================================================
   JM Byline row — Figma "Molecules / Byline row" (437:624) and
   "Molecules / Byline row · Mobile" (637:4580)

   One rule set covers both molecules. The desktop one is a nowrap row; Figma's
   own note on the mobile one calls that a defect ("whose fixed internal layout
   does not reflow below ~560"), so this wraps at every width. Above ~560 it
   fits on one line and is pixel-identical to the desktop molecule; below, it
   behaves like the mobile one.
   ========================================================================= */

.jm-byline {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 0.5rem;            /* 8 — Figma's gap, and also the width of the
                                    two spaces the mobile molecule sets around
                                    each dot at 13px */
  row-gap: 0.25rem;              /* 4 — Figma never draws a wrapped byline, so
                                    this is our number */
  font: 400 var(--text-label-s)/1.3 var(--font-sans);   /* Label/S 13 */
  color: var(--purple-400);
}

/* Break between parts, never inside one: "Just Mercy Trust" wrapping onto two
   lines is the failure the mobile molecule exists to avoid. */
.jm-byline__item,
.jm-byline__sep {
  white-space: nowrap;
}

/* =========================================================================
   JM Post grid — the dynamic card grid

   Same card, queried rather than hand-placed. The tracks match the boards:
   The Facts' Latest insights row is 3 x 410.67 at a 24 column gap and a 40 row
   gap (430:533 / 430:555), and Partnerships' case studies row is 4 x 302 at 24
   (483:2650). Both are `repeat(N, minmax(0, 1fr))` inside the 1280 measure, which
   reproduces those widths exactly.
   ========================================================================= */

.jm-post-grid {
  display: grid;
  column-gap: 1.5rem;                  /* 24 */
  row-gap: 2.5rem;                     /* 40 — the board's row gap, not 24 */
  align-items: stretch;
}

.jm-post-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.jm-post-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.jm-post-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

@media (max-width: 61.99rem) {
  .jm-post-grid--3,
  .jm-post-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 47.99rem) {
  .jm-post-grid--2,
  .jm-post-grid--3,
  .jm-post-grid--4 { grid-template-columns: minmax(0, 1fr); }
}

/* An empty grid tells the reader nothing; this says why it is empty. */
.jm-post-grid__empty {
  margin: 0;
  font: 400 var(--text-body)/1.4 var(--font-sans);
  color: var(--purple-400);
}

/* A pill that leads to its type archive. It keeps the pill's shape exactly; only
   the pointer and a hover step say it is a link, because a category label that
   suddenly looked like body-copy link text would fight the card's own title. */
.jm-tag--link {
  text-decoration: none;
  transition: background-color var(--motion-fast) var(--motion-ease-out);
}

.jm-tag--link:hover { background-color: var(--gold-600); }
.jm-tag--link:focus-visible { outline: 3px solid var(--purple-500); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .jm-tag--link { transition: none; }
}

/* =========================================================================
   Overset desktop slider (Phil, 2026-08-31) — jm-post-grid--overset.

   Above the mobile breakpoint the grid becomes a sideways scroller: the
   columns' worth of cards fit the row and the next card peeks under a faded
   right edge, so the overflow is visible instead of hidden. Below 48rem the
   ordinary jm-swipe mobile track applies unchanged.

   The fade is a mask on the scroller (border-box, so it stays put while the
   content scrolls). Where scroll-driven animations exist, the fade lifts as
   the row reaches its end — a fade over the final card would otherwise say
   "more" when there is none. Browsers without them keep a constant fade,
   which is only ever slightly wrong at the very end.
   ========================================================================= */

@property --jm-overset-fade {
  syntax: '<length>';
  inherits: false;
  /* px, not rem, and deliberately: the spec requires a registered property's
     initial-value to be computationally independent, so rem/em/vw are refused
     and Chrome drops the WHOLE rule ("Ignored @property rule"). Unregistered,
     the keyframes below interpolate discretely and the fade snapped off at
     92.5% instead of lifting. The element rule and the keyframes still speak
     rem; this value only ever applies when nothing has set the property. */
  initial-value: 80px;
}

@keyframes jm-overset-unfade {
  0%, 85% { --jm-overset-fade: 5rem; }
  100%    { --jm-overset-fade: 0rem; }
}

@media (min-width: 48rem) {
  #brx-content .jm-post-grid--overset {
    display: flex;
    flex-wrap: nowrap;
    align-items: stretch;
    /* Explicit width, never auto: the parent Bricks block lays children out
       at align-items: flex-start, and under it an auto-width nowrap flex row
       shrinks to fit-content — the row rendered 856px inside a 1330px
       container, four cramped columns, no overflow, no fade (the same trap
       swipe.css documents for the mobile track). */
    width: 100%;
    gap: 1.5rem;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    --jm-overset-fade: 5rem;
    mask-image: linear-gradient(to right, #000 calc(100% - var(--jm-overset-fade)), rgb(0 0 0 / 0) 100%);
  }
  #brx-content .jm-post-grid--overset::-webkit-scrollbar { display: none; }

  #brx-content .jm-post-grid--overset > * {
    /* Two cards plus the ~5.5rem peek until the four-column width exists. */
    flex: 0 0 calc((100% - 1.5rem - 5.5rem) / 2);
    min-width: 0;
    max-width: none;
    scroll-snap-align: start;
    /* Equal heights, flush bottoms (Phil, 2026-08-31): align-self beats the
       element-level align-items: flex-start Bricks writes on its #brxe rule,
       and height:auto neutralises the card's grid-era height:100%, which in a
       flex row resolves against nothing and left every card content-height. */
    align-self: stretch;
    height: auto;
  }

  @supports (animation-timeline: scroll(self inline)) {
    #brx-content .jm-post-grid--overset {
      animation: jm-overset-unfade linear both;
      animation-timeline: scroll(self inline);
    }
  }
}

@media (min-width: 62rem) {
  #brx-content .jm-post-grid--overset > * {
    /* Four full cards, three 1.5rem gaps, the fifth peeking ~5.5rem (4rem clears the fade). */
    flex-basis: calc((100% - 3 * 1.5rem - 5.5rem) / 4);
  }
}
