/* Testimonials — a horizontal strip that moves with the scroll.
   Modelled on flowty's "What do users think of Flowty?", measured at 1440x900.

   flowty's numbers:
     header  72px / line-height 67.68 (0.94) / left aligned / full width
             one continuous statement, first sentence in ink and the rest grey
     card    587px design -> 440 rendered, radius 45, py 60 / px 45
     stars   21px · meta 12.75px · name 21px · quote 16.5px / lh 21.78
             (flowty carries a DATE here and a verified dot; the owner cut the
              dot and the tenure, keeping the device)
     row     flex, gap 22.5, w-max (2754 — far wider than the viewport)

   The strip is PINNED (§8.5): the section rides up normally, sticks when its
   top reaches the top of the viewport, and from there the vertical scroll
   drives the row sideways. It releases the moment the last card is fully
   inside the viewport. flowty's strip is also draggable — ours is not, by
   the owner's call. */

.testi {
  position: relative;
  z-index: 2;
  overflow: clip;                 /* the row is wider than the page */
  /* Fills the screen because it is pinned: a section shorter than the viewport
     would stick with dead space under it. flowty's is `min-h-screen` too. */
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--sp-24) 0;
  background: var(--bg-3);
  color: var(--text);
}

.testi__head {
  max-width: 24ch;
  margin: 0 var(--gutter) var(--sp-24);
  font-size: var(--fs-testi);
  line-height: 0.94;
  letter-spacing: -0.02em;
}
/* flowty carries the second half of the statement in grey — the claim reads in
   full ink, the invitation underneath it recedes. */
.testi__head em {
  font-style: normal;
  color: var(--text-muted);
}

.testi__row {
  display: flex;
  /* `flex-start` let every card size to its own quote, so the strip was a
     ragged run of different heights — the shortest review made a visibly
     stubbier card than the longest sitting right beside it. `stretch` (the flex
     default) makes every card as tall as the tallest in the row, which is what
     a strip of cards scrolling past one another needs. The card is a flex
     column below so the content still starts at the top and the extra height
     collects at the bottom, rather than being shared out between the blocks. */
  align-items: stretch;
  gap: var(--sp-6);
  width: max-content;
  padding-inline: var(--gutter);
  will-change: transform;
}

.testi__card {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  width: clamp(280px, 34vw, 480px);
  padding: clamp(28px, 3.6vw, 56px);
  border-radius: clamp(26px, 3.2vw, 46px);
  /* White on white: the border is the only thing defining these, so it carries
     a little more weight than --hair-soft does elsewhere. No shadow — buttons
     and cards on this site are flat. */
  background: var(--secondary);
  border: 1px solid var(--hair-card);
}

.testi__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
}
.testi__stars {
  display: flex;
  gap: 0.12em;
  font-size: var(--fs-md);
  line-height: 1;
  color: var(--brand);
}
/* Device only — the owner cut the tenure ("8 months", "1 year") that sat with it. */
.testi__meta {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  white-space: nowrap;
}

.testi__who {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-8);
}
.testi__avatar {
  display: grid;
  place-items: center;
  width: var(--sp-10);
  height: var(--sp-10);
  flex-shrink: 0;
  overflow: hidden;
  border-radius: 999px;
}
.testi__avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;   /* the portraits are square, but never trust that */
}
/* Every reviewer has a portrait now. If one ever ships without, give that
   avatar a fill and drop the initials in as text — do NOT reuse another
   reviewer's photo, which is what the first batch accidentally did. */
.testi__name {
  font-size: var(--fs-md);
  font-family: var(--font-head);
  font-weight: var(--font-head-weight);
}
.testi__quote {
  margin-top: var(--sp-6);
  font-size: var(--fs-lead);
  line-height: 1.32;
  color: var(--text-muted);
}

/* Portrait counts as stacked here too — see the note in css/hero.css. A 1024px
   wide iPad in portrait is past the width line but is shaped like a tall phone,
   and was taking the desktop layout. */
@media (max-width: 1023px), (orientation: portrait) {
  .testi { padding: var(--sp-20) 0; }
  .testi__head { margin-bottom: var(--sp-16); max-width: none; }
}
@media (max-width: 767px) {
  /* ONE CARD AT A TIME, FRAMED.
     The card was clamp(280px, 34vw, 480px), which on a 375px screen resolves to
     the 280px floor. With 24px of row padding and a 24px gap that leaves the
     next card starting at 328px — so at every point in the strip's travel you
     were reading a card with its neighbour sliced down the right edge, and
     mid-travel a card was cut on BOTH sides at once. That is the "cards going
     out of the viewport" report: at 375px nothing is ever whole.

     Sized against the viewport instead, one card fills the frame and the next
     shows a deliberate sliver — the affordance that says the strip moves. The
     pin distance is measured from row.scrollWidth in dom/testimonials.js, so it
     follows this automatically and the last card still lands flush. */
  .testi__card {
    width: calc(100vw - var(--gutter) - var(--sp-12));
  }
  .testi { padding: var(--sp-16) 0; }
  .testi__card { padding: var(--sp-8); }
}
