/**
 * Review wall — masonry client-review grid (template-parts/review-wall.php).
 * ---------------------------------------------------------------------------
 * OWNER LAYER, not ported design CSS. This section has no Claude Design source;
 * it was specified from the Relume "testimonial-18" reference and rebuilt in the
 * theme's own tokens. It lives OUTSIDE assets/css/design/ so that tree stays
 * pristine per CLAUDE.md, and outside theme-overrides.css because it is a new
 * component rather than a deviation from the design.
 *
 * All sizing is rem/clamp so the section inherits the fluid 1440→4K root from
 * assets/css/scale.css. px is kept only where the theme's rules require it:
 * @media conditions, box-shadow, and transforms.
 */

/* ---------- Section intro ---------- */
.fr-rw-intro { text-align: center; max-width: 46rem; margin: 0 auto clamp(2.25rem, 4vw, 3.25rem); }
.fr-rw-heading { margin: 0.875rem 0 0; font-size: clamp(2.2rem, 3.4vw, 3.2rem); }
.fr-rw-chip { display: flex; justify-content: center; margin-top: 1.375rem; }

/* ---------- Masonry ----------
   CSS multicol (as in the reference). `inline-block` + `width:100%` on the card
   is the long-standing Safari fix that stops multicol dropping/splitting a
   break-inside:avoid child. */
.fr-rw-viewport { position: relative; }
.fr-rw-grid { columns: 3; column-gap: clamp(1.25rem, 2vw, 2rem); }

/* Collapsed height is clipped on the WRAPPER, not on the multicol box.
   Two reasons:
   1. Multicol balances by content height, so with ragged quote lengths the three
      columns finish up to ~200px apart. A fade alone can't hide that — the short
      column's hard bottom edge lands in the fade's semi-transparent zone, where
      a DEEPER fade only makes it more visible. Clipping the wrapper cuts all
      three columns on one flat line, mid-card, which is the effect we want.
   2. Putting the height on the multicol box itself instead would make it
      fragment: content spills into extra columns in the inline direction and
      whole cards get clipped away rather than cut. The wrapper keeps the grid at
      its natural height, so it still balances normally.

   Height tuned against the real 17-review set with cards at full length
   (432-1240px): 88rem (1408px at 1440) leaves five complete reviews visible and
   cuts through all three columns, so the bottom edge is flat. Shorter values
   left one column ending above the line with a gap beside the cut.

   HEADROOM: `overflow:hidden` clips the hover state too — row-one cards sit
   flush with the wrapper's top edge, so the 3px hover lift AND the ~8px upward
   throw of --shadow-lg were being shaved off (and the same horizontally on the
   outer columns). The padding opens the clip box 1rem on the top and sides; the
   matching negative margin cancels it so nothing moves. Bottom stays at 0 —
   that edge must keep clipping hard, or cards would bleed BELOW the fade and
   show as a hard line. box-sizing is border-box, hence max-height carries the
   +1rem. */
.fr-rw:not(.is-expanded) .fr-rw-viewport {
  max-height: calc(88rem + 1rem);
  overflow: hidden;
  padding: 1rem 1rem 0;
  margin: -1rem -1rem 0;
}

/* Border + hover match the theme's standard card treatment (`.cta-step` in
   testimonials.css / home.css): a 1.5px transparent border painted via
   padding-box/border-box gradients, which on hover swaps the static border
   colour for the animated conic flow. Own @property + keyframes so the
   component is self-contained if the wall is ever reused on a page whose CSS
   doesn't declare them — same approach chip.css takes. */
@property --fr-angle { syntax: "<angle>"; initial-value: 0deg; inherits: false; }
@keyframes fr-rw-border-flow { to { --fr-angle: 360deg; } }

.fr-rw-card {
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  page-break-inside: avoid;
  display: inline-block;
  width: 100%;
  margin: 0 0 clamp(1.25rem, 2vw, 2rem);
  padding: clamp(1.5rem, 2.2vw, 2rem);
  border: 0.09375rem solid transparent;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  background: linear-gradient(var(--surface), var(--surface)) padding-box,
              linear-gradient(var(--border), var(--border)) border-box;
  transition: transform .2s var(--ease-out), box-shadow .2s var(--ease-out);
}
.fr-rw-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
  background: linear-gradient(var(--surface), var(--surface)) padding-box,
              conic-gradient(from var(--fr-angle), var(--cyan), var(--blue-action), var(--blue), var(--cyan)) border-box;
  animation: fr-rw-border-flow 3.2s linear infinite;
}

.fr-rw-stars { display: flex; gap: 0.375rem; color: var(--cyan); margin-bottom: 1.125rem; }
/* The star SVGs carry px width/height ATTRIBUTES, which don't track the fluid
   root — so size them in rem here or they stay 18px on a 4K viewport while
   everything around them grows (the same conversion scale.css does for the
   design's inline icons). */
.fr-rw-stars svg { width: 1.125rem; height: 1.125rem; }

.fr-rw-quote {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--fw-regular);
  font-size: 1.0625rem;
  line-height: 1.5;
  color: var(--text-strong);
}
.fr-rw-quote p { margin: 0; }
.fr-rw-quote p + p { margin-top: 0.75em; }

/* NO per-card clamp / "read more" (removed 2026-08-04, owner call). A per-card
   expand fought the wall clip: opening a long card inside the collapsed wall
   grew it into the clipped region, so it stayed cut off behind the fade and the
   text still couldn't be read. Cards now run their full length and the masonry
   absorbs the ragged heights, which is what multicol is for. */

.fr-rw-by {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  margin-top: 1.375rem;
  padding-top: 1.25rem;
  border-top: 0.0625rem solid var(--border);
}
/* Optional per-card client logo — normalized the same way as the marquee
   (uniform box, greyscale, white knocked out via multiply) so mixed source
   shapes sit on one baseline. Off by default; see review-wall.php. */
.fr-rw-logo { flex: none; width: 2.75rem; height: 2.75rem; object-fit: contain; filter: grayscale(1); opacity: 0.7; mix-blend-mode: multiply; }
.fr-rw-who { display: flex; flex-direction: column; gap: 0.125rem; min-width: 0; }
.fr-rw-name { font-weight: var(--fw-semibold); font-size: 0.9375rem; color: var(--text-strong); line-height: 1.3; }
/* One verbatim string covering title + credentials + practice, so it has to
   wrap gracefully — some run to a full line of post-nominals. */
.fr-rw-attr { font-size: 0.8125rem; color: var(--text-muted); line-height: 1.4; }

/* Deliberately indistinguishable from the surrounding text (owner's call), so
   it has to beat the global `a` / `a:hover` colour rules in the page CSS — hence
   naming :hover and :focus explicitly rather than relying on inheritance.
   :focus-visible is the exception: a link that is invisible to the eye must
   still be findable by keyboard, and that ring only ever shows for keyboard
   users, never on click. */
.fr-rw-attr-link,
.fr-rw-attr-link:hover,
.fr-rw-attr-link:focus { color: inherit; text-decoration: none; }
.fr-rw-attr-link:focus-visible { outline: 0.125rem solid var(--cyan); outline-offset: 0.125rem; border-radius: 0.125rem; }

/* ---------- Cap, fade and reveal ----------
   Over-cap cards stay in the DOM (indexed) and are hidden until
   assets/js/site.js `initReviewWall` puts `.is-expanded` on the wall.
   `--over` is the desktop overflow set and is a SUBSET of `--over-mob`, so the
   mobile rule alone covers both below the single-column breakpoint. */
.fr-rw:not(.is-expanded) .fr-rw-card--over { display: none; }

/* Softens the clip line above. The wrapper clip is what makes the bottom edge
   flat; this only has to blend the last ~9rem of legible text into the page. */
.fr-rw-fade {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: clamp(7rem, 12vw, 10rem);
  pointer-events: none;
  /* Explicit rgba stops, not `transparent` — some engines interpolate the
     keyword through transparent BLACK and haze the gradient grey. */
  background: linear-gradient(180deg, rgba(246, 246, 245, 0) 0%, rgba(246, 246, 245, 0.86) 46%, rgba(246, 246, 245, 1) 86%);
  background: linear-gradient(180deg,
      color-mix(in srgb, var(--paper) 0%, transparent) 0%,
      color-mix(in srgb, var(--paper) 86%, transparent) 46%,
      var(--paper) 86%);
}

.fr-rw-more { display: flex; justify-content: center; margin-top: clamp(0.5rem, 1.5vw, 1rem); }
.fr-rw.is-expanded .fr-rw-fade,
.fr-rw.is-expanded .fr-rw-more { display: none; }

/* ---------- Breakpoints ---------- */
@media (max-width: 1024px) { .fr-rw-grid { columns: 2; } }
/* Set overflows the mobile cap only — nothing is hidden above the single-column
   breakpoint, so don't offer to reveal it there. */
@media (min-width: 701px) {
  .fr-rw--cap-mobile-only .fr-rw-fade,
  .fr-rw--cap-mobile-only .fr-rw-more { display: none; }
  /* The clip MUST lift wherever the button is suppressed — otherwise a set this
     size gets cut off on desktop with no control left to expand it.
     `.fr-rw` is repeated to match the specificity of the `:not(.is-expanded)`
     rule above (0,3,0); being later in the file is then what wins it. */
  .fr-rw.fr-rw--cap-mobile-only .fr-rw-viewport { max-height: none; overflow: visible; }
}
@media (max-width: 700px) {
  .fr-rw-grid { columns: 1; }
  .fr-rw:not(.is-expanded) .fr-rw-card--over-mob { display: none; }
  /* Single column, so there is no inter-column spread — the clip is purely to
     cut the last card mid-sentence, which is what signals "there's more".
     At full card length ~500-1100px here, so 62rem (992px) clears the first
     review and cuts well into the second. */
  .fr-rw:not(.is-expanded) .fr-rw-viewport { max-height: calc(62rem + 1rem); }
  .fr-rw-fade { height: clamp(5rem, 18vw, 8rem); }
}

@media (prefers-reduced-motion: reduce) {
  .fr-rw-card, .fr-rw-card:hover { transition: none; transform: none; }
  .fr-rw-card:hover { animation: none !important; }
}
