/* ============================================================
   Block: program_cards — 3-up hairline grid with line-art icons.
   Design source: H:131-166. Mobile-first per DECISIONS D3.

   Uses the 1px-gap grid-with-background trick (design-system.md
   3.3) at rule alpha .4, which is Programs' specific weight —
   most other grids in the design use .3.
   ============================================================ */

.m434-programs-head { margin-bottom: 28px; }

.m434-programs-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 28px;
  line-height: 1.06;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 10px 0 0;
}

/* ---------- The hairline grid ----------
   Single column on mobile, so the "gap is the rule" trick draws
   horizontal rules between stacked cards with no extra markup.

   The rule is drawn by each CARD's own 1px ring rather than by a
   background on the container showing through the gaps. Both produce
   the identical line when a row is full, and only this one survives a
   row that is not: a container background paints every empty cell as a
   solid gray block, so four programs in a three-column grid would
   render one card beside two gray slabs. Three programs fill the grid
   exactly today, which is the only reason that was never seen here — it
   WAS seen on the team grid, and this is the same fix applied to the
   same technique before it bites twice.

   box-shadow-as-border is this theme's border idiom (global.css:88). */
.m434-hairgrid.m434-programs-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1px;
}

.m434-hairgrid.m434-programs-grid > .m434-program {
  box-shadow: 0 0 0 1px var(--m434-line-3);
}

/* This card is a LIGHT ISLAND: the fill is unconditional, so choosing
   the "Navy" or "Accent" band in the editor leaves a white card on a
   dark section. blocks.css owns that pattern for the whole theme —
   it restores --ink on the card's headings and re-declares the surface
   tokens (link / accent / muted / focus ink) so no `.dark-bg …` rule
   reaches inside. Nothing block-specific is needed here; if this
   selector is ever renamed, rename it in that list too.
   Before that block existed .m434-program-title rendered #ffffff on
   #ffffff — 1.00:1, literally invisible. */
.m434-program {
  display: flex;
  flex-direction: column;
  background: var(--white);
  padding: 26px 22px;
  transition: background 0.2s var(--m434-ease);
  /* Containing block for .m434-program-card__link::after below. The two
     MUST stay together: an inset:0 overlay with no positioned ancestor
     resolves against the initial containing block and covers the whole
     viewport, which is a full-page invisible link over every other
     control. Never delete one without the other. */
  position: relative;
}

.m434-program:hover { background: var(--m434-hover-light); }

.m434-program-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 18px;
}

.m434-program-icon,
.m434-program-icon-img {
  display: block;
  width: 46px;
  height: 46px;
  flex: 0 0 auto;
}

/* currentColor is the whole reason these are inline SVG rather than
   uploads — the icon follows the accent token with no re-export. */
.m434-program-icon { color: var(--red); }
/* --red #E5761E is 3.03:1 on the card's --white rest ground, clearing 1.4.11's
   3:1 for a non-text graphic by 0.03. The hover ground --m434-hover-light
   #f4f5f7 drops it to 2.77:1, so the icon and the arrowlink's masked arrow both
   fall under threshold exactly when the pointer is on them. Deepening to
   --m434-accent-text (5.23:1 on that ground) fixes it and reads as intentional:
   the card's accent darkens as it lifts. */
.m434-program:hover .m434-program-icon,
.m434-program:hover .m434-arrowlink::before { color: var(--m434-accent-text); }
.m434-program-icon svg { width: 100%; height: 100%; display: block; }

.m434-program-badge {
  flex: 0 0 auto;
  font-family: var(--body);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--white);
  background: var(--m434-crimson);
  padding: 5px 9px;
}

.m434-program-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 19px;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 12px;
}

/* ---- The card's title link ----------------------------------------
   inc/post-type-program.php moved the destination onto an <a> inside
   the <h3> (so the link's accessible name is the program name and
   nothing else) and demoted the label at the foot of the card to an
   aria-hidden <span>. It documented the CSS that arrangement needs and
   reported it, because this file was out of that round's scope; this is
   that CSS. Measured before it landed, the anchor took the global
   `a { color: var(--m434-link-ink) }` and rendered #a34f0e where its own
   <h3> is --ink #303030 — a lone orange heading in a grid of ink ones —
   and the hit area was the 255x53px of the title text inside a
   370x417px card, i.e. 8.8% of the target the design draws.

   `color: inherit` puts it back on --ink (13.20:1 on the card's --white,
   12.10:1 on the --m434-hover-light it fades to). Both the value it
   replaces and this one clear AA, so this is a design-fidelity and
   target-size fix, not a contrast one — the ratios are recorded so the
   next person does not have to re-measure to find that out.

   The ::after is the standard stretched link: it expands the anchor's
   hit area to the whole card without wrapping the card in an <a>, which
   would fold the icon, the badge, the body copy and the label into the
   accessible name. Same pattern, same reasoning, as
   .m434-post-card__link::after in archive.css. */
.m434-program-card__link {
  color: inherit;
  text-decoration: none;
}
.m434-program-card__link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

.m434-program-body {
  font-family: var(--body);
  font-size: 14px;
  line-height: 1.6;
  color: var(--m434-slate-strong);
  margin: 0;
}

/* margin-top:auto pins the link to the bottom so the three cards'
   links align regardless of body length (the design's behavior). */
.m434-program-link {
  margin-top: auto;
  padding-top: 20px;
  align-self: flex-start;
  font-family: var(--body);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* ---- Accent link color ----------------------------------------
     12px/700 is NORMAL text by WCAG 2.1, so the threshold is 4.5:1 —
     the 3:1 large-text relaxation does not apply and --red (#E5761E,
     3.03:1 on white) is not available at any size here. Never swap
     this back to --red (tokens.css, defect F-16).

     It was reading muddy because --m434-accent-text is much darker
     than it needs to be: #a34f0e is 5.70:1 on --white and 5.23:1 on
     the #f4f5f7 the card hovers to, not the 5.70:1 on white (the
     documented figure is arithmetically wrong — see the report). That
     left better than a full stop of unused headroom, so the label is
     warmed toward the accent by the largest amount BOTH grounds can
     still carry:

       color-mix(in srgb, var(--red) 20%, var(--m434-accent-text))
         = #b05711 -> 4.98:1 on --white, 4.57:1 on --m434-hover-light

     25% would give #b45912 and 4.40:1 in the hover state, i.e. an AA
     failure in a reachable state, so 20% is the ceiling. The mix is
     written as a second declaration with the plain token first: any
     engine without color-mix() keeps the old, still-compliant value
     rather than falling back to inherited color. Both operands are
     tokens, so nothing new is defined here. */
  --m434-program-link-ink: var(--m434-accent-text);
  --m434-program-link-ink: color-mix(in srgb, var(--red) 20%, var(--m434-accent-text));
  color: var(--m434-program-link-ink);
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
  transition: letter-spacing 0.2s var(--m434-ease);
  /* 44px min touch target without visually padding the text. */
  min-height: 44px;
  display: inline-flex;
  align-items: flex-end;
}

/* The color is restated on the interactive states, at (0,3,0), for a
   cascade reason and not a visual one: global.css's
   `.m434-arrowlink:hover` weighs (0,2,0) and beat the (0,1,0) rest
   rule above, so the warmed mix was silently discarded the moment the
   pointer landed and the label fell back to the plain token. Both
   values clear AA, so this was cosmetic — but the reasoning written
   above was only true at rest, which is the kind of comment that
   stops being true without anyone noticing. Reading the same custom
   property keeps the two in step. */
.m434-program-link:hover,
.m434-program-link:focus-visible { letter-spacing: 0.14em; }
.m434-program-link.m434-arrowlink:hover,
.m434-program-link.m434-arrowlink:focus-visible { color: var(--m434-program-link-ink); }

/* ---- Leading arrow ----------------------------------------------
   ASSUMPTION, flagged in the report: global.css is gaining a shared
   .m434-arrowlink for this "-> LABEL" shape, and program-cards.php
   will compose it onto .m434-program-link. No arrow is drawn here —
   duplicating it would collide with that class the moment it lands.
   What this file contributes is the one thing the shared class cannot
   know: on a white card the arrow may be full --red. It is pure
   ornament next to a label that already names the destination, and
   WCAG 2.1 exempts decorative glyphs from 1.4.3, so the 4.5:1 floor
   that pins the label down does not pin the arrow down — which is
   what makes the link read brighter without spending contrast.

   Both hooks are inert until .m434-arrowlink exists, so this is safe
   to ship ahead of it; whichever one that class actually reads, the
   other is a no-op. If it turns out to draw the arrow some third way,
   reconcile by pointing it at --red here rather than re-coloring the
   label. */
/* :not(.is-inert) — the third card renders as a <span>, correctly
   absent from the accessibility tree because it has nowhere to go, and
   an unscoped rule still drew it a full-accent leading arrow beside a
   deliberately-muted label. An affordance painted on something that is
   not one is worse than no affordance. */
.m434-program-link.m434-arrowlink:not(.is-inert) { --m434-arrowlink-color: var(--red); }
.m434-program-link.m434-arrowlink:not(.is-inert)::before { color: var(--red); }

/* A label with nowhere to go: styled as text, not as an affordance.
   --slate is #6b7280: 4.83:1 on --white but 4.43:1 on the
   --m434-hover-light the card fades to, which is an AA failure at
   12px in a state the pointer can reach. --m434-slate-strong is
   7.56:1 / 6.93:1 on those two grounds and reads the same weight. */
.m434-program-link.is-inert {
  color: var(--m434-slate-strong);
  cursor: default;
}

/* ---- Co-existence with the stretched link ----
   A REAL control inside the card has to sit above the overlay or the
   overlay swallows its clicks. A modal card renders its label as a
   <button data-m434-modal-open> (post-type-program.php), and the <h3>
   anchor and that button are set by INDEPENDENT conditions — $link_url
   and $is_modal — so a card carrying both is reachable through the
   editor even though no page composes one today. Without this lift the
   modal would simply stop opening on such a card, silently.

   Scoped to real controls on purpose. The decorative label is a <span>
   and must STAY under the overlay: it names the same destination, so a
   click on it should follow the title link, which is exactly what
   happens while it stays beneath. Lifting it would make the one word
   an editor thinks is the link the one place the card is dead. */
.m434-program :is(a, button):not(.m434-program-card__link) {
  position: relative;
  z-index: 2;
}

/* ---- Focus relocated to the card ----
   The hit area is now the whole card, so the focus indicator has to be
   the whole card too — a ring drawn around the title text alone would
   point at 8.8% of what the Enter key activates. Identical mechanism,
   guard and color token as archive.css's .m434-post-item ring: :has()
   moves it to the container and suppresses the anchor's own, and where
   :has() is unsupported the whole block is skipped and the global
   :focus-visible ring stays on the anchor — smaller, but present.
   Never invert this; the ring must exist by default and be RELOCATED
   by the enhancement, not created by it.

   --m434-focus-ring is #111 on light grounds: 18.88:1 on the card's
   --white and 17.31:1 on the --m434-hover-light it fades to under the
   pointer, so it holds in the ordinary tab-then-hover state. On a
   Navy or Accent band blocks.css re-declares the token for the light
   island, so the ring follows the card rather than the section. */
@supports selector(:has(*)) {
  .m434-program:has(.m434-program-card__link:focus-visible) {
    outline: 2px solid var(--m434-focus-ring);
    outline-offset: -2px;
  }
  .m434-program-card__link:focus-visible { outline: none; }
}
.m434-program-link.is-inert:hover { letter-spacing: 0.08em; }

@media (min-width: 768px) {
  .m434-programs-head { margin-bottom: 34px; }
  .m434-programs-title { font-size: 34px; }
  .m434-hairgrid.m434-programs-grid { grid-template-columns: 1fr 1fr; }
  .m434-program { padding: 30px 26px; }
}

@media (min-width: 1024px) {
  .m434-programs-title { font-size: 40px; }
  .m434-hairgrid.m434-programs-grid { grid-template-columns: 1fr 1fr 1fr; }
  .m434-program { padding: 34px 28px; }
  .m434-program-title { font-size: 20px; }
}
