/* ===========================================================================
   The six-card "why" grid, plus the cursor-tracking light effect.
   =========================================================================== */

.v2 .cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}
@media (min-width: 640px)  { .v2 .cards { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .v2 .cards { grid-template-columns: repeat(3, 1fr); } }

/* ===== Card shell =====
   Separated from the canvas by a hairline, not a shadow — the reference is
   explicit that surfaces step apart by border and surface colour rather than
   by drop shadow, and mixing the two is what makes a dark UI look muddy. */
.v2 .card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--bg-surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-card);
  padding: var(--s-4) var(--s-4) 0;
  overflow: hidden;
  isolation: isolate;
  transition: border-color 0.25s ease, background 0.25s ease;
}

/* ===== Cursor light, layer 1: the interior spotlight =====
   A wide, very low-alpha coral wash centred on the pointer. --mx/--my are
   written per-card by card-spotlight.js in the card's own coordinate space.

   Fallback values matter: with no JS (or before the first mousemove) the
   gradient still resolves to the card's centre instead of collapsing to 0 0
   and lighting the top-left corner. */
.v2 .card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(
    420px circle at var(--mx, 50%) var(--my, 50%),
    rgba(221, 120, 103, 0.16),
    transparent 62%
  );
  opacity: 0;
  transition: opacity 0.28s ease;
  pointer-events: none;
  z-index: 0;
}

/* ===== Cursor light, layer 2: the border glow =====
   The hairline brightens on the edge nearest the pointer. Built with the
   padding + mask-composite ring trick: a 1px-padded box whose own content area
   is masked out, leaving only the border ring painted by the gradient.

   Scoped to the hovered card only (see the .is-lit rules below), so the ring
   traces the pointer around the edge of that one card rather than the whole
   grid responding at once. */
.v2 .card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: radial-gradient(
    300px circle at var(--mx, 50%) var(--my, 50%),
    rgba(221, 120, 103, 0.65),
    rgba(108, 192, 255, 0.28) 40%,
    transparent 68%
  );
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
          mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  opacity: 0;
  transition: opacity 0.28s ease;
  pointer-events: none;
  z-index: 2;
}

/* Only the card actually under the pointer lights. Moving between cards
   crossfades — the one being left fades out over the same 0.28s as the one
   being entered fades in — because both keep their own coordinates.

   Both conditions are required, and each does a distinct job: `:hover` picks
   the single card, and `.is-lit` (added by the JS) confirms the effect is
   running at all. Hover alone would light the card on touch devices and under
   prefers-reduced-motion, where the JS deliberately never engages — CSS
   :hover has no idea the effect was opted out of. */
.v2 .cards.is-lit .card:hover::before          { opacity: 1; }
.v2 .cards.is-lit .card:hover::after           { opacity: 1; }
.v2 .cards.is-lit .card:hover .card-shot::after { opacity: 1; }

/* The card directly under the pointer lifts slightly out of the grid.
   `transition` here is safe alongside the scroll reveal because the reveal is
   an @keyframes animation, not a transition — see base.css. */
.v2 .card:hover { border-color: var(--hairline-strong); }

/* ===== Card content ===== */
/* z-index lifts content above the ::before wash, which sits at z-index 0. */
.v2 .card-body {
  position: relative;
  z-index: 1;
  flex: 1 1 auto;
}
.v2 .card-body h3 { margin-bottom: var(--s-1); }
.v2 .card-body p {
  color: var(--text-muted);
  font-size: 15px;
  line-height: 1.6;
  text-wrap: pretty;
}

/* ===== Screenshot, bleeding off the bottom edge =====
   Negative side margins pull it out to the card's inner edges; the card's own
   overflow:hidden does the cropping. Fixed height with object-position at the
   top means the crop always keeps the top of the capture, which is the part
   framed to be legible. */
.v2 .card-shot {
  position: relative;
  z-index: 1;
  margin: var(--s-4) calc(var(--s-4) * -1) 0;
  height: 176px;
  border-top: 1px solid var(--hairline);
  background: var(--bg-surface-alt);
  overflow: hidden;
}
.v2 .card-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}
/* Missing capture: keep the framed area, drop the broken-image icon. */
.v2 .card-shot.is-missing img { display: none; }

/* ===== Cursor light, layer 3: the light carried across the screenshot =====
   The shot is opaque and stacks above the card's ::before wash, so without this
   the light stopped dead at the screenshot's top edge and the card read as two
   separate surfaces with a seam between them.

   --mx/--my here are written directly onto .card-shot by card-spotlight.js in
   the SHOT's own coordinate space, shadowing the values it would otherwise
   inherit from the card — so the light lands in the same physical place on
   screen as it does on the card around it, and the two halves stay continuous.

   screen blending brightens the dark capture toward coral instead of laying a
   flat tint over it, which is what makes it read as light falling on the image
   rather than a coloured film. The card's own `isolation: isolate` keeps the
   blend contained to the card and off the page behind it. */
.v2 .card-shot::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    420px circle at var(--mx, 50%) var(--my, 50%),
    rgba(221, 120, 103, 0.22),
    transparent 62%
  );
  mix-blend-mode: screen;
  opacity: 0;
  transition: opacity 0.28s ease;
  pointer-events: none;
  z-index: 2;
}

@media (prefers-reduced-motion: reduce) {
  .v2 .card,
  .v2 .card::before,
  .v2 .card::after,
  .v2 .card-shot::after { transition: none; }
}
