/* The match wall (module M4): every mutual match of the night on one wall of
   portraits. The autopilot works through them one card per beat (no drift);
   a person's hover, focus, drag or swipe takes the wall over. Portraits are
   always shown in full, natural colour (docs/26).
   Faces carry no signal colour: each card's ink foot is its night-log entry
   (minute, state, AI lamp), and only a match handed to AI lights that foot. */
.match-wall {
  --match-card-width: 300px;
  --match-card-height: 400px;
  --match-foot: 58px;
  --match-gap: 20px;
  /* Card 1 rests on the text column; the wall bleeds off the far edge. */
  --wall-inset: max(var(--gutter), (100% - 1440px) / 2);
  padding-block: clamp(60px, 5.6vw, 88px) clamp(44px, 3.9vw, 64px);
  background: var(--ink);
}

/* ── Head: one heading at poster scale, the intro under it ───────────── */
.match-wall-head { margin-block-end: clamp(26px, 2.5vw, 40px); }
.match-wall-head h2 {
  font-size: var(--h2-l);
  line-height: .93;
  letter-spacing: -.03em;
}
.match-wall-head h2 > span { display: block; color: var(--faint); }
/* Machine voice: the heading runs at the night log's width (Archivo, Latin). */
.match-wall-head h2:not(:is(:lang(zh), :lang(ja), :lang(ko), :lang(ar), :lang(he), :lang(ru), :lang(uk), :lang(bg), :lang(mk), :lang(sr), :lang(kk), :lang(el), :lang(ka), :lang(hi), :lang(bn), :lang(ta), :lang(te), :lang(th), :lang(km))) {
  font-stretch: var(--w-log);
}
:is(:lang(zh), :lang(ja), :lang(ko)) .match-wall-head h2 { line-height: 1.1; }
.match-wall-intro {
  max-inline-size: 34em;
  margin-block-start: clamp(14px, 1.3vw, 20px);
  font-size: 17px;
  line-height: 1.65;
  color: var(--muted);
}

/* ── Viewport ─────────────────────────────────────────── */
.match-viewport {
  /* Keep the wall's positive scroll coordinates and physical arrow keys in
     every language; card text still follows the interface direction. */
  direction: ltr;
  inline-size: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
  touch-action: pan-x pan-y;
  scroll-behavior: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--wall-inset);
  /* A card read by the autopilot fades out as it passes the text column. */
  -webkit-mask-image: linear-gradient(90deg, transparent calc(var(--wall-inset) - var(--match-gap)), #000 var(--wall-inset));
  mask-image: linear-gradient(90deg, transparent calc(var(--wall-inset) - var(--match-gap)), #000 var(--wall-inset));
}
.match-viewport::-webkit-scrollbar { display: none; }
/* Without JS there are no cards: drop the empty scroller (and its tab stop). */
@media (scripting: none) { .match-viewport { display: none; } }
.match-viewport:focus-visible { outline-offset: -4px; -webkit-mask-image: none; mask-image: none; }
.match-viewport.is-dragging, .match-viewport.is-dragging * { cursor: grabbing; user-select: none; }
/* While a hand moves the wall (and until its glide lands) nothing snaps. */
.match-viewport.is-free { scroll-snap-type: none; }
.match-track {
  display: flex;
  gap: var(--match-gap);
  inline-size: max-content;
  /* The wall is built when the page is idle; its place is held until then. */
  min-block-size: var(--match-card-height);
  padding-inline: var(--wall-inset);
}
.match-group { display: flex; gap: var(--match-gap); list-style: none; padding: 0; margin: 0; }

/* ── Card ─────────────────────────────────────────────── */
.connection-card {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 0 0 var(--match-card-width);
  inline-size: var(--match-card-width);
  block-size: var(--match-card-height);
  border-radius: 12px;
  background: var(--ink-2);
  overflow: hidden;
  isolation: isolate;
  cursor: pointer;
  scroll-snap-align: start;
}
[dir=rtl] .connection-card { direction: rtl; }

.connection-figure {
  position: relative;
  flex: 1 1 auto;
  min-block-size: 0;
  overflow: hidden;
  background: var(--ink-3);
  /* Portraits far along the wall are not laid out or painted until they near
     the screen. (Only the figure: the foot, with the live typing dots, always
     keeps its style current, so the switch pauses them everywhere.) */
  content-visibility: auto;
  contain-intrinsic-size: auto var(--match-card-width) auto calc(var(--match-card-height) - var(--match-foot));
}
.connection-photo {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: center 30%;
  transition: scale 1s var(--ease), opacity var(--t-ui) var(--ease);
  -webkit-user-drag: none;
  user-select: none;
}
/* A face fades in once its file has arrived; before that (no source yet, or
   still loading) the card shows its ink plate, never alt text or a broken icon. */
.connection-photo:not(.is-loaded) { opacity: 0; }
/* The scrim under the name and caption. */
.connection-figure::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(180deg, rgb(11 13 18 / 0) 46%, rgb(11 13 18 / .6) 70%, rgb(11 13 18 / .92));
}

@media (hover: hover) {
  .connection-card:hover .connection-photo { scale: 1.03; }
}

.connection-details { position: absolute; z-index: 2; inset-inline: 18px; inset-block-end: 16px; color: var(--bone); }
.connection-title { display: flex; align-items: baseline; gap: 9px; font: 800 30px/1.05 var(--font-display); letter-spacing: -.025em; }
.connection-title span { font: 400 17px/1 var(--font-display); font-variant-numeric: tabular-nums; letter-spacing: 0; color: var(--bone); }
.connection-caption { margin-block-start: 6px; font-size: 13px; line-height: 1.5; color: rgb(242 240 234 / .86); }
/* Interests: plain words that wrap, never squeezed into an ellipsis. */
.connection-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 0; margin-block-start: 8px; font-size: 12px; line-height: 1.4; color: rgb(242 240 234 / .74); }
.connection-tags > span:not(:last-child)::after { content: ""; display: inline-block; inline-size: 3px; block-size: 3px; margin-inline: 8px; border-radius: 50%; background-color: currentColor; vertical-align: middle; opacity: .7; }

/* ── The foot: this match's line in the night log ─────── */
.connection-foot {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  flex: none;
  min-block-size: var(--match-foot);
  padding-block: 7px;
  padding-inline: 18px 7px;
  background: var(--ink-1);
  box-shadow: inset 0 1px 0 var(--line);
}
/* Handed to AI, the foot's top edge lights in the signal colour. */
.connection-foot::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  block-size: 2px;
  background-color: var(--signal);
  pointer-events: none;
  scale: 0 1;
  transform-origin: 0 50%;
  transition: scale var(--t-ui) var(--ease), background-color .5s var(--ease);
}
[dir=rtl] .connection-foot::before { transform-origin: 100% 50%; }
.connection-card.is-ai .connection-foot::before { scale: 1 1; }

/* The minute this match landed: machine time, condensed. */
.connection-time {
  flex: none;
  direction: ltr;
  unicode-bidi: isolate;
  font: 700 14px/1 var(--font-display);
  font-stretch: var(--w-machine);
  font-variant-numeric: tabular-nums lining-nums;
  letter-spacing: .04em;
  color: var(--faint);
  transition: color var(--t-ui) var(--ease);
}
.connection-card.is-reading .connection-time { color: var(--bone); }
.connection-matched {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 auto;
  min-inline-size: 0;
  font: 600 12px/1.25 var(--font-text);
  color: var(--muted);
  transition: color .5s var(--ease);
}
.connection-matched svg { inline-size: 13px; block-size: 13px; stroke-width: 2.4; }
/* A long status wraps to a second line rather than losing its end. */
.connection-status { min-inline-size: 0; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; overflow-wrap: anywhere; }
.connection-card.is-ai .connection-matched { color: var(--signal); }
.connection-card.is-ai .connection-matched svg { display: none; }

/* AI is typing: three dots, paused with the autopilot and offscreen (--run). */
.typing-dots { display: none; flex: none; align-items: center; gap: 3px; margin-inline-start: 1px; }
.connection-card.is-ai .typing-dots { display: inline-flex; }
.typing-dots i { inline-size: 3px; block-size: 3px; border-radius: 50%; background-color: currentColor; animation: match-typing 1.2s ease-in-out infinite; animation-play-state: var(--run, running); }
.typing-dots i:nth-child(2) { animation-delay: .15s; }
.typing-dots i:nth-child(3) { animation-delay: .3s; }

/* Hand this match to AI: a 44 px lamp button. Pressed, it fills bone; the
   signal colour stays on the foot, never over the face. */
.connection-ai {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  flex: none;
  inline-size: 44px;
  block-size: 44px;
  margin-inline-start: auto;
  padding: 0;
  border-radius: 50%;
  background-color: transparent;
  box-shadow: inset 0 0 0 1px var(--line-2);
  color: var(--bone);
  font: 800 12px/1 var(--font-display);
  letter-spacing: .02em;
  cursor: pointer;
  transition: background-color var(--t-micro) var(--ease), color var(--t-micro) var(--ease), box-shadow var(--t-micro) var(--ease), scale var(--t-micro) var(--ease);
}
.connection-ai-lamp { inline-size: 6px; block-size: 6px; border-radius: 50%; box-shadow: inset 0 0 0 1.5px currentColor; transition: background-color var(--t-micro) var(--ease); }
.connection-ai[aria-pressed=true] { background-color: var(--bone); box-shadow: inset 0 0 0 1px var(--bone); color: var(--ink); }
.connection-ai[aria-pressed=true] .connection-ai-lamp { background-color: currentColor; }
.connection-ai:active { scale: .9; }
/* The hand-off: one bone ring leaves the button as it is pressed (transform and opacity only). */
.connection-ai::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: inset 0 0 0 1.5px var(--bone); opacity: 0; pointer-events: none; }
.connection-ai[aria-pressed=true]::after { animation: match-handoff .8s cubic-bezier(.25, .8, .35, 1) both; }
.connection-ai:focus-visible { outline-offset: 2px; }
@media (hover: hover) {
  .connection-ai:hover { box-shadow: inset 0 0 0 1px var(--bone); }
  .connection-ai[aria-pressed=false]:hover { background-color: var(--ink-4); }
}

/* ── Rail: the wall's own pause, the night tape, the AI count, the CTA ── */
.match-wall-rail { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 28px; margin-block-start: clamp(16px, 1.5vw, 22px); container-type: inline-size; }

/* The wall's own pause: a transport control (a ring with the pause bars or
   the play triangle, and a constant label), never signal red. aria-pressed and the
   icon say whether the wall is paused. */
.match-motion {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  flex: none;
  min-block-size: 44px;
  padding: 0;
  padding-inline-end: 4px;
  border-radius: 999px;
  font: 500 14px/1.2 var(--font-text);
  color: var(--bone);
  white-space: nowrap;
}
.match-motion-icon {
  position: relative;
  flex: none;
  inline-size: 44px;
  block-size: 44px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px var(--line-2);
  transition: box-shadow var(--t-micro) var(--ease), background-color var(--t-micro) var(--ease), scale var(--t-micro) var(--ease);
}
.match-motion:hover .match-motion-icon { box-shadow: inset 0 0 0 1px var(--bone); background-color: rgb(242 240 234 / .06); }
.match-motion:active .match-motion-icon { scale: .94; }
.match-motion-icon::before { content: ""; position: absolute; inset: 16px 17px; background: linear-gradient(90deg, currentColor 0 3px, transparent 3px 7px, currentColor 7px); }
.match-motion[aria-pressed=true] .match-motion-icon::before { inset-inline: 18px 16px; background: currentColor; clip-path: polygon(0 0, 100% 50%, 0 100%); }
[dir=rtl] .match-motion[aria-pressed=true] .match-motion-icon::before { clip-path: polygon(100% 0, 0 50%, 100% 100%); }

/* The night tape: the 30 matches from 23:52 to 05:40, one tick each. The
   tick at the reading position is bone; a match handed to AI stands taller in
   the signal colour. Left to right like the wall, in every language. */
.match-tape {
  display: flex;
  align-items: center;
  gap: 14px;
  flex: 1 1 180px;
  min-inline-size: 0;
  max-inline-size: 640px;
  direction: ltr;
  font: 700 13px/1 var(--font-display);
  font-stretch: var(--w-machine);
  font-variant-numeric: tabular-nums lining-nums;
  letter-spacing: .04em;
  color: var(--faint);
}
.match-tape-scale { display: flex; justify-content: space-between; align-items: center; flex: 1; min-inline-size: 0; block-size: 18px; }
.match-tape-scale i { inline-size: 1px; block-size: 18px; background-color: var(--line-2); scale: 1 .45; transition: background-color var(--t-ui) var(--ease), scale var(--t-ui) var(--ease); }
.match-tape-scale i.is-ai { scale: 1 .78; background-color: var(--signal); }
.match-tape-scale i.is-here { scale: 1 1; background-color: var(--bone); box-shadow: 0 0 0 .5px var(--bone); }

.match-readout { display: inline-flex; align-items: center; gap: 9px; flex: none; direction: ltr; unicode-bidi: isolate; font: 500 12px/1 var(--font-mono); letter-spacing: .08em; color: var(--faint); white-space: nowrap; }
.match-readout .lamp { --lamp: 7px; margin-inline-end: 3px; }
.match-readout:not(.is-live) .lamp { background-color: var(--faint); }
.match-readout-figures { margin-inline-start: 2px; }
.match-readout b { font-weight: 500; color: var(--bone); }
.match-wall-rail .arrow-link { margin-inline-start: auto; }
/* A narrow rail keeps one row: the tape gives way first. */
@container (max-width: 860px) { .match-tape { display: none; } }
@media (scripting: none) { .match-wall-rail .arrow-link { margin-inline-start: 0; } }
.match-noscript { font-size: 14px; padding-block: 20px; color: var(--muted); }

@keyframes match-handoff {
  from { opacity: .7; transform: scale(1); }
  to { opacity: 0; transform: scale(1.9); }
}
@keyframes match-typing {
  0%, 70%, 100% { opacity: .3; }
  35% { opacity: 1; }
}

@media (min-width: 1700px) {
  .match-wall { --match-card-width: 330px; --match-card-height: 460px; --match-gap: 24px; }
}

@media (max-width: 1000px) {
  .match-wall { --match-card-width: 250px; --match-card-height: 360px; --match-foot: 56px; --match-gap: 16px; }
  .connection-title { font-size: 27px; }
  .connection-foot { padding-inline-start: 14px; gap: 10px; }
  /* The pause keeps its name (and tooltip) but shows only its ring, so the rail stays one row. */
  .match-motion { padding: 0; }
  .match-motion-label { position: absolute; clip-path: inset(50%); width: 1px; height: 1px; overflow: hidden; white-space: nowrap; }
}

@media (max-width: 760px) {
  /* Chinese keeps its two clauses as two lines on a phone. */
  .match-wall { --match-card-width: 220px; --match-card-height: 320px; --match-foot: 54px; --match-gap: 12px; }
  .match-wall-intro { font-size: 15px; }
  .connection-card { border-radius: 10px; }
  .connection-details { inset-inline: 14px; inset-block-end: 12px; }
  .connection-title { font-size: 24px; gap: 7px; }
  .connection-title span { font-size: 15px; }
  .connection-caption { margin-block-start: 4px; font-size: 12px; line-height: 1.45; }
  .connection-tags { margin-block-start: 6px; font-size: 11px; }
  .connection-tags > span:not(:last-child)::after { margin-inline: 6px; }
  .connection-foot { gap: 8px; padding-block: 5px; padding-inline: 12px 5px; }
  .connection-time { font-size: 13px; }
  .connection-matched { font-size: 11px; gap: 5px; }
  /* Phones: one row, the CTA and then the wall's pause; the tape and the
     count are instruments for wider screens. */
  .match-wall-rail { flex-wrap: nowrap; column-gap: 16px; }
  .match-wall-rail .arrow-link { flex: 0 1 auto; min-inline-size: 0; margin-inline-start: 0; }
  .match-motion { order: 1; margin-inline-start: auto; }
  .match-tape, .match-readout { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .connection-photo { transition: none; }
  .connection-card:hover .connection-photo { scale: none; }
  .typing-dots i { animation: none; opacity: 1; }
  .connection-ai[aria-pressed=true]::after { animation: none; }
}
