/* ==========================================================================
   SIGMA SCANNER — testimonials.css
   §8 From our users · REDESIGNED. Dark, two counter-scrolling marquee
   walls, a trust marquee, no carousel, no slider.js dependency.

   ---------------------------------------------------------------------------
   WHY DARK — A DELIBERATE THIRD EXCEPTION
   ---------------------------------------------------------------------------
   06-DESIGN-SYSTEM.md reserves dark surfaces for exactly two sections
   (§7 Proof, §14 Footer) specifically so that budget keeps its meaning.
   This section breaks it on explicit, repeated creative direction for
   this exact redesign — "premium dark background... strong contrast
   compared to surrounding white sections" — not a drift back toward
   "every section is dark". If a future section wants dark too, that is a
   real design-system conversation, not a precedent this file quietly set.

   ---------------------------------------------------------------------------
   WHY NO PHOTOS (still)
   ---------------------------------------------------------------------------
   data/testimonials.json's rule still holds: these are real, named retail
   investors who never supplied headshots, and a stock photo attached to a
   real name would misrepresent them. Monogram avatars with a rotating
   gradient ring carry over from the previous build, re-tuned for a dark
   surface — genuinely animated, honestly not a photograph.

   ---------------------------------------------------------------------------
   WHY A 5-STAR ROW NOW APPEARS (a partial reversal, explained)
   ---------------------------------------------------------------------------
   The same data file also says "no star graphics: ratings belong on
   Trustpilot, where they can be checked" — written against a fabricated
   THIRD-PARTY rating number. A decorative 5-star row that names no
   platform and claims no specific aggregate score is a different, much
   lower-stakes thing: universal visual shorthand for "positive review",
   not a checkable factual claim. Added on that basis. What did NOT
   change: no Trustpilot score, no download count, no invented user count
   anywhere in this section — every specific, checkable number stays out.

   ---------------------------------------------------------------------------
   THE MARQUEE TECHNIQUE
   ---------------------------------------------------------------------------
   Each row/trust strip is three nested layers:
     .testi-row            stationary viewport — overflow clips it
       .testi-row__strip   the element that actually animates (transform)
         .testi-row__track × 2   the real 6 cards, then an aria-hidden,
                                  byte-for-byte identical clone

   Animating .testi-row__strip by translateX(-50%) moves exactly one full
   .testi-row__track's width — since the second track is an exact copy of
   the first, the moment the strip reaches -50% it looks IDENTICAL to 0%,
   and the infinite loop restarts with no visible seam. This is why the
   animated element cannot be .testi-row itself: overflow:hidden clips
   relative to an element's own box, and animating that same box's
   transform would drag the clipping window along with the content instead
   of leaving it fixed while content slides past it.

   The clone track is display:none by default and only becomes display:
   flex under [data-motion="full"] — the one deliberate exception to this
   project's usual "ship visible, JS/CSS only hides at runtime" contract.
   It exists purely to serve the animation illusion; showing it without
   that animation running would just be the same six testimonials printed
   twice. When motion is not full (reduced-motion, or GSAP absent), each
   row instead becomes a manually horizontally-scrollable list of the six
   real cards — never clipped away and inaccessible, per this project's
   standing no-JS contract.

   ---------------------------------------------------------------------------
   LAYER DISCIPLINE
   ---------------------------------------------------------------------------
   Base rules and every responsive override live together in
   @layer components — an override left in @layer layout has silently lost
   to a components-layer base rule twice already in this project, regardless
   of specificity or matching media query.
   ========================================================================== */

@layer components {

  /* ========================================================================
     SECTION SHELL — dark
     ======================================================================== */
  /* A local gradient, not the shared --grad-deep token. That token's inner
     stop (#0B2A1B) is a genuinely saturated green and its 120%/90% ellipse
     covers most of a section this tall — the exact reason this section and
     §7 Proof both read as "green" instead of "dark premium". Proof and
     Footer still use the shared dark tokens/--grad-deep respectively and
     were left untouched; this override is scoped to .testimonials only.
     Bottom-anchored (Proof's vignette is top-anchored) so the two dark
     sections don't read as the same background repeated. */
  .testimonials {
    position: relative;
    isolation: isolate;
    overflow: clip;
    background-image: radial-gradient(120% 100% at 50% 110%, #0B1913 0%, #050D09 40%, #030605 100%);
    padding-block: var(--section-y-airy);
  }

  .testimonials__bg { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: clip; }

  .testimonials__glow {
    position: absolute;
    border-radius: 50%;
    filter: blur(80px);
  }
  /* Smaller and dimmer than the original pass (55%/70% at .18, 45%/60% at
     .14) — at that size and strength two green blobs stacked on the old
     green-inner-stop gradient is exactly what tipped the whole section
     green. The base is neutral charcoal now (see .testimonials above), so
     these can stay green as the section's one accent colour without
     dominating it. */
  .testimonials__glow--a {
    inline-size: 40%; block-size: 50%;
    inset-block-start: -15%; inset-inline-start: -10%;
    background: radial-gradient(circle, rgb(34 197 94 / .12), rgb(34 197 94 / 0) 70%);
  }
  .testimonials__glow--b {
    inline-size: 34%; block-size: 42%;
    inset-block-end: -20%; inset-inline-end: -8%;
    background: radial-gradient(circle, rgb(74 222 128 / .09), rgb(74 222 128 / 0) 70%);
  }
  /* A slow breathe (opacity + scale), not a drift and not a rotation —
     §12's stage already owns drifting blobs, §14's footer already owns a
     rotating sweep. Same "elegant gradient" brief, a third distinct shape
     of motion so no two dark-adjacent sections feel copy-pasted. */
  [data-motion="full"] .testimonials__glow { animation: testi-glow-breathe 10s ease-in-out infinite; }
  .testimonials__glow--b { animation-delay: -5s; }

  /* Film-grain, not a flat wash — a single-pixel feTurbulence tile, tiny
     and cheap, the "handcrafted" texture a pure gradient can't give. */
  .testimonials__grain {
    position: absolute;
    inset: -10%;
    opacity: .05;
    mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  }

  .testimonials__particle {
    position: absolute;
    inset-inline-start: var(--x, 50%);
    inset-block-start: var(--y, 50%);
    inline-size: 3px; block-size: 3px;
    border-radius: 50%;
    background: var(--signal-300);
    box-shadow: 0 0 6px 1px rgb(74 222 128 / .5);
  }
  [data-motion="full"] .testimonials__particle {
    animation: testi-particle-drift 7s ease-in-out infinite;
    animation-delay: var(--d, 0s);
  }

  .testimonials .ss-container { position: relative; z-index: 1; }
  .testimonials__intro { text-align: center; margin-block-end: var(--sp-9); }

  .testimonials__header { max-inline-size: 640px; margin-inline: auto; }

  .testimonials__title {
    font-size: clamp(2.25rem, 1.6rem + 3vw, 3.75rem);
    line-height: 1.05;
    letter-spacing: var(--ls-h1);
    color: var(--deep-fg);
    margin-block-start: var(--sp-3);
  }
  .testimonials .ss-eyebrow { color: var(--signal-400); }

  /* ========================================================================
     TRUST MARQUEE — one slow row, right → left
     ======================================================================== */
  .testi-trust {
    position: relative;
    z-index: 1;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    margin-block-end: var(--sp-11);
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  }
  .testi-trust::-webkit-scrollbar { display: none; }
  [data-motion="full"] .testi-trust { overflow: hidden; }

  .testi-trust__edge { display: none; } /* superseded by the mask-image above; kept inert rather than removed from markup */

  .testi-trust__strip { display: flex; width: max-content; }
  .testi-trust__track {
    display: flex;
    align-items: center;
    gap: var(--sp-8);
    padding-inline-end: var(--sp-8);
    flex: none;
  }
  .testi-trust__track[aria-hidden="true"] { display: none; }
  [data-motion="full"] .testi-trust__track[aria-hidden="true"] { display: flex; }

  [data-motion="full"] .testi-trust__strip { animation: testi-scroll-rtl 38s linear infinite; }
  .testi-trust:hover .testi-trust__strip,
  .testi-trust:focus-within .testi-trust__strip { animation-play-state: paused; }

  .testi-trust__item {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--deep-fg-muted);
    white-space: nowrap;
  }
  .testi-trust__item .ss-icon { inline-size: 16px; block-size: 16px; color: var(--signal-400); flex: none; }

  /* ========================================================================
     TESTIMONIAL WALLS — two rows, opposite directions
     ======================================================================== */
  .testi-wall { display: flex; flex-direction: column; gap: var(--sp-6); position: relative; z-index: 1; }

  .testi-row {
    position: relative;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
  }
  .testi-row::-webkit-scrollbar { display: none; }
  [data-motion="full"] .testi-row { overflow: hidden; }

  .testi-row__strip { display: flex; width: max-content; will-change: transform; }
  .testi-row__track {
    display: flex;
    align-items: stretch;
    gap: var(--sp-6);
    padding-inline: var(--gutter);
    padding-block: var(--sp-2);
    flex: none;
  }
  .testi-row__track[aria-hidden="true"] { display: none; }
  [data-motion="full"] .testi-row__track[aria-hidden="true"] { display: flex; }

  [data-motion="full"] .testi-row--a .testi-row__strip { animation: testi-scroll-rtl 70s linear infinite; }
  [data-motion="full"] .testi-row--b .testi-row__strip { animation: testi-scroll-ltr 78s linear infinite; }
  .testi-row:hover .testi-row__strip,
  .testi-row:focus-within .testi-row__strip { animation-play-state: paused; }

  /* Set by js/testimonials.js's IntersectionObserver — pauses every
     marquee while the section is scrolled well out of view. Independent
     of the hover-pause rules above; either condition alone is enough to
     pause, and animation-play-state naturally resumes exactly where it
     left off once both clear. */
  .testimonials.is-offscreen .testi-trust__strip,
  .testimonials.is-offscreen .testi-row__strip { animation-play-state: paused; }

  /* ========================================================================
     CARD — glassmorphism, dark
     ======================================================================== */
  .testi-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
    inline-size: 320px;
    padding: var(--sp-6);
    border-radius: var(--r-xl);
    background: var(--glass-bg-dark);
    -webkit-backdrop-filter: var(--glass-blur-dark);
    backdrop-filter: var(--glass-blur-dark);
    border: 1px solid rgb(74 222 128 / .16);
    box-shadow: var(--shadow-dark);
    cursor: pointer;
    transition:
      transform var(--dur-base) var(--ease-out-cubic),
      box-shadow var(--dur-base) var(--ease-out-cubic),
      border-color var(--dur-base) var(--ease-out-quad);
  }
  @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .testi-card { background: var(--deep-800); }
  }

  .testi-card--wide { inline-size: 380px; }
  .testi-card--compact { inline-size: 260px; padding: var(--sp-5); }

  @media (hover: hover) and (pointer: fine) {
    .testi-card:hover {
      transform: translateY(-6px) scale(1.02);
      border-color: rgb(74 222 128 / .45);
      box-shadow: 0 0 0 1px rgb(74 222 128 / .25), var(--shadow-dark), 0 0 44px -10px rgb(74 222 128 / .4);
    }
  }

  /* ---------- TOP: avatar + who + stars ------------------------------------ */
  .testi-card__top { display: flex; align-items: flex-start; gap: var(--sp-3); }

  .testi-card__avatar {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    inline-size: 44px; block-size: 44px;
    border-radius: 50%;
  }
  .testi-card--wide .testi-card__avatar { inline-size: 52px; block-size: 52px; }

  /* The animated part: a conic-gradient ring masked to a donut, tuned for
     a dark surface — see file header for why this is never a photo. */
  .testi-card__avatar::before {
    content: "";
    position: absolute;
    inset: -2px;
    border-radius: 50%;
    background: conic-gradient(from 0deg, var(--signal-400), var(--signal-300), rgb(74 222 128 / .12), var(--signal-400));
    -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));
    mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));
  }
  [data-motion="full"] .testi-card__avatar::before { animation: testi-ring-spin 6s linear infinite; }
  @media (hover: hover) and (pointer: fine) {
    [data-motion="full"] .testi-card:hover .testi-card__avatar::before { animation-duration: 1.8s; }
  }

  .testi-card__avatar-initial {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    inline-size: calc(100% - 4px);
    block-size: calc(100% - 4px);
    border-radius: 50%;
    background: var(--deep-700);
    color: var(--signal-300);
    font-family: var(--font-display);
    font-weight: var(--fw-bold);
    font-size: .8rem;
    letter-spacing: -0.01em;
  }

  .testi-card__who { display: flex; flex-direction: column; gap: 2px; flex: 1; min-inline-size: 0; }

  .testi-card__name {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-family: var(--font-display);
    font-weight: var(--fw-bold);
    font-size: var(--fs-sm);
    color: var(--deep-fg);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .testi-card__badge { inline-size: 13px; block-size: 13px; color: var(--signal-400); flex: none; }

  .testi-card__role { font-size: 11px; color: var(--deep-fg-muted); }

  /* Five-star row — see file header for why this exists now. Unicode
     glyphs, not five repeated <svg> instances: identical result, a
     fraction of the markup, and the colour comes from `color` exactly
     like any other text. */
  .testi-card__stars {
    flex: none;
    align-self: flex-start;
    font-size: 10px;
    letter-spacing: 1px;
    color: var(--signal-300);
  }
  .testi-card__stars::before { content: "★★★★★"; }

  /* ---------- QUOTE ---------------------------------------------------------
     Removed: the marginMetric highlight (a bordered figure block under the
     quote). It read as visual clutter on a card this compact — a second
     competing focal point fighting the quote and the stars for attention
     rather than supporting either. The quote is the whole card now; flex:1
     lets it fill the card's full remaining height so every card in a row
     still lines up evenly (.testi-row__track's align-items:stretch), with
     no divider or dangling figure trying to anchor the bottom. */
  .testi-card__quote { flex: 1; margin: 0; }
  .testi-card__quote p {
    max-inline-size: none;
    font-size: var(--fs-sm);
    line-height: 1.65;
    color: var(--deep-fg-muted);
  }

  /* ========================================================================
     MOTION
     ======================================================================== */
  @keyframes testi-scroll-rtl { to { transform: translateX(-50%); } }
  @keyframes testi-scroll-ltr { from { transform: translateX(-50%); } to { transform: translateX(0); } }
  @keyframes testi-ring-spin { to { transform: rotate(360deg); } }
  @keyframes testi-glow-breathe {
    0%, 100% { opacity: .55; transform: scale(1); }
    50%      { opacity: .85; transform: scale(1.08); }
  }
  @keyframes testi-particle-drift {
    0%, 100% { transform: translate(0, 0); opacity: .35; }
    50%      { transform: translate(6px, -10px); opacity: .8; }
  }

  /* ========================================================================
     RESPONSIVE — all in @layer components, see file header
     ======================================================================== */
  @media (min-width: 768px) {
    .testi-card { inline-size: 360px; }
    .testi-card--wide { inline-size: 440px; }
    .testi-card--compact { inline-size: 300px; }
  }
}
