/* ============================================================
   03 — How it works
   Three plates of one height and three different widths, threaded
   by a rail that passes behind them.
   Colour legend, taught in 01, reused in 02 and 03. Same key 04 uses
   interactively: everything Ripple touches is violet, deepest at the source
   and lighter as it fans out. Coral is not a semantic here — the page spends
   it on error and on the watermark 04 strips.
     violet-700 = the source post you actually made
     violet     = destinations and completed actions
     ink        = connective / neutral
     amber      = in flight, not yet published
   ============================================================ */

.how {
  --how-gap-col: clamp(var(--s-6), 3.2vw, var(--s-10));
  --how-gap-row: var(--s-14);         /* stacked row gutter; the mobile thread spans exactly this */
  --how-split: var(--s-8);            /* plate | copy gutter, 560–991 */
  --how-rail: color-mix(in srgb, var(--accent) 46%, transparent);
  --how-rail-end: min(var(--how-gap-col), var(--gutter));   /* never breaches the page gutter */
  --how-node: var(--s-2);
  --how-ghost: color-mix(in srgb, var(--accent) 66%, transparent);  /* 3.3:1 on the wash */
}

.how__steps {
  display: grid;
  gap: var(--how-gap-row) var(--how-gap-col);
  margin-top: clamp(var(--s-12), 5vw, var(--s-20));
}

/* Per-step composition. Below the three-across breakpoint each plate takes
   its own artwork's proportion, so nothing is letterboxed. In the row, height
   is shared instead — the caption baseline and the rail then land in one
   place and the difference between plates comes from the unequal columns. */
.how__step {
  --plate-ratio: 240 / 204;
  --wash-x: 6%;
  --wash-y: 100%;
  --wash-tilt: 168deg;
  --num-size: min(var(--fs-h1), 30cqi);
  position: relative;
  display: grid;
  gap: var(--s-6);
}

.how__step:nth-child(2) {
  --plate-ratio: 240 / 222;
  --wash-x: 96%;
  --wash-y: 6%;
  --wash-tilt: 196deg;
}

.how__step:nth-child(3) {
  --plate-ratio: 240 / 226;
  --wash-x: 50%;
  --wash-y: 42%;
  --wash-tilt: 148deg;
}

/* ---------- The illustrated plate ---------- */
.how__stage {
  position: relative;
  display: grid;
}

.how__plate {
  position: relative;
  z-index: 1;
  container-type: inline-size;
  display: grid;
  aspect-ratio: var(--plate-ratio);
  padding: var(--s-5);
  overflow: hidden;
  background:
    radial-gradient(115% 95% at var(--wash-x) var(--wash-y), var(--accent-soft) 0%, transparent 62%),
    linear-gradient(var(--wash-tilt), var(--sand-100) 0%, var(--surface) 60%);
}

/* Ghosted folio. Same corner, same size, on every step: bottom-right of its
   own plate, sitting on the plate's own padding — the same margin the artwork
   keeps — so the digits are contained by the card, clear of its corner radius,
   and can never reach a neighbouring card. The two subtractions are optical:
   0.035em is the trailing letter-spacing and 0.13em is the empty space between
   the digit baseline and the bottom of a line box at line-height 1. Cancelling
   both puts the *ink* exactly one padding-step off each edge, rather than the
   box. Each artwork keeps that corner clear, so the folio never collides with
   the illustration. */
.how__num {
  position: absolute;
  z-index: 0;
  inset: auto calc(var(--s-5) - 0.035em) calc(var(--s-5) - 0.13em) auto;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  font-family: var(--font-display);
  font-weight: var(--fw-black);
  font-size: var(--num-size);
  line-height: 1;
  letter-spacing: var(--tracking-display);
  font-variant-numeric: tabular-nums;
  color: var(--how-ghost);
}

.how__art {
  position: relative;
  z-index: 1;
  grid-area: 1 / 1;
  width: 100%;
  height: 100%;
}

/* ---------- Copy ---------- */
.how__title {
  font-size: var(--fs-h3);
}

.how__copy {
  margin-top: var(--s-3);
  color: var(--text-muted);
}

/* ============================================================
   Thread — stacked: down through the row gutter, ending on a node
   ============================================================ */
.how__step:not(:last-child)::before,
.how__step:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 50%;
  pointer-events: none;
}

.how__step:not(:last-child)::after {
  top: 100%;
  height: var(--how-gap-row);
  border-left: 1px dashed var(--how-rail);
  transform: translateX(-50%);   /* centre the 1px rule on the same axis as the node */
}

.how__step:not(:last-child)::before {
  top: calc(100% + var(--how-gap-row));
  z-index: 2;
  width: var(--how-node);
  height: var(--how-node);
  border-radius: var(--r-pill);
  background: var(--accent);
  transform: translate(-50%, -50%);
}

/* ============================================================
   SVG palette (decoration only, inline art)
   ============================================================ */
.how__acc   { fill: var(--accent); }
.how__acc2  { fill: var(--violet-400); }
.how__acc3  { fill: var(--violet-300); }
.how__soft  { fill: var(--accent-soft); }
.how__mut   { fill: var(--ink-200); }
.how__src   { fill: var(--violet-700); }      /* source only */
.how__softs { fill: var(--violet-100); }      /* source ground */
.how__amber { fill: var(--amber-500); }       /* pending only */
.how__surf  { fill: var(--surface); }

.how__strs,
.how__strn,
.how__slot,
.how__dash,
.how__spark,
.how__check,
.how__checki {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.how__strs   { stroke: var(--violet-700); stroke-width: 2; }
.how__strn   { stroke: var(--border-strong); stroke-width: 2; }
.how__slot   { stroke: var(--border-strong); stroke-width: 2; stroke-dasharray: 5 5; }
.how__dash   { stroke: var(--how-rail); stroke-width: 2; stroke-dasharray: 2 8; }
.how__spark  { stroke: var(--accent); stroke-width: 2.5; }
.how__check  { stroke: var(--accent-contrast); stroke-width: 2.5; }  /* on --accent, 8.6:1 */
.how__checki { stroke: var(--accent); stroke-width: 2.5; }           /* on --accent-soft */

/* The source post travels the rule, once, when the step reveals. No loop. */
.how__pulse { fill: var(--accent); opacity: 0; }

.how__step.is-in .how__pulse {
  animation: how-flow var(--dur-slower) var(--ease-out) var(--dur-slow) 1 both;
}

@keyframes how-flow {
  0%   { transform: translateX(0);    opacity: 0; }
  16%  { transform: translateX(6px);  opacity: 1; }
  78%  { transform: translateX(40px); opacity: 1; }
  100% { transform: translateX(40px); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .how__step.is-in .how__pulse { animation: none; opacity: 1; transform: translateX(40px); }
}

/* ============================================================
   560–991 — plate and copy share a row, thread runs down the plates
   ============================================================ */
@media (min-width: 35rem) and (max-width: 61.99rem) {
  .how__step {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: center;
    gap: var(--how-split);
  }

  .how__step:not(:last-child)::before,
  .how__step:not(:last-child)::after {
    left: calc(25% - var(--how-split) / 4);
  }
}

/* ============================================================
   992+ — three across, unequal columns, one rail
   ============================================================ */
@media (min-width: 62rem) {
  .how__steps {
    grid-template-columns: 1.05fr 0.95fr 1.15fr;   /* 03 carries the payoff */
    grid-template-rows: auto auto;
  }

  /* Plates share a height, so the caption baseline and the rail agree. */
  .how__step {
    grid-row: span 2;
    grid-template-rows: subgrid;
    row-gap: var(--s-6);        /* subgrid inherits the parent gutter otherwise */
  }

  .how__plate {
    aspect-ratio: auto;
    height: clamp(15rem, 24vw, 22.5rem);
  }

  /* The stacked thread has no job here. Matched specificity so it is
     actually cancelled. */
  .how__step:not(:last-child)::before,
  .how__step:not(:last-child)::after { content: none; }

  /* The rail. Each element owns the gap to its RIGHT, so every dash-phase
     reset happens behind an opaque plate and each visible span is one
     uniformly pitched segment. */
  .how__stage::before {
    content: "";
    position: absolute;
    z-index: 0;
    top: 50%;
    left: 0;
    right: 0;
    border-top: 1px dashed var(--how-rail);
    pointer-events: none;
  }

  .how__step:not(:last-child) .how__stage::before {
    right: calc(var(--how-gap-col) * -1);
  }

  .how__step:first-child .how__stage::before {
    left: calc(var(--how-rail-end) * -1);
    -webkit-mask-image: linear-gradient(90deg, transparent, var(--ink-900) var(--how-rail-end));
    mask-image: linear-gradient(90deg, transparent, var(--ink-900) var(--how-rail-end));
  }

  .how__step:last-child .how__stage::before {
    right: calc(var(--how-rail-end) * -1);
    -webkit-mask-image: linear-gradient(270deg, transparent, var(--ink-900) var(--how-rail-end));
    mask-image: linear-gradient(270deg, transparent, var(--ink-900) var(--how-rail-end));
  }

  /* Bead where the rail arrives at a plate. Nothing arrives at plate 01. */
  .how__step:not(:first-child) .how__stage::after {
    content: "";
    position: absolute;
    z-index: 2;
    top: 50%;
    left: 0;
    width: var(--how-node);
    height: var(--how-node);
    border-radius: var(--r-pill);
    background: var(--accent);
    transform: translate(-50%, -50%);
  }
}
