/* ──────────────────────────────────────────────────────────────
   Recommendations — auto-scrolling LinkedIn social proof.

   Design language follows .claude/skills/high-end-visual-design,
   translated to this repo's stack (plain CSS + Webflow export, no
   Tailwind) and to the type system already established on the site
   (Fraunces 300 headings / Martian Mono body). Vibe archetype:
   Soft Structuralism — which is what the site already is, so the
   craft goes into depth and motion rather than a new palette.

   Cards are a fixed size on purpose: uniform height keeps the
   marquee reading as one calm band instead of a ragged skyline.
   ────────────────────────────────────────────────────────────── */

.rec {
  --rec-gap: 1.25rem;
  --rec-card-w: 24rem;
  --rec-fade: 9rem;
  --rec-duration: 60s;

  /* Concentric radii for the double-bezel: the inner core's corner is
     the outer radius minus the shell padding, so the curves nest. */
  --rec-shell-radius: 1.75rem;
  --rec-shell-pad: 0.375rem;
  --rec-core-radius: calc(var(--rec-shell-radius) - var(--rec-shell-pad));

  /* Heavy, slow-out spring. Everything interactive uses this. */
  --rec-ease: cubic-bezier(0.32, 0.72, 0, 1);
}

/* Macro-whitespace: this section breathes more than the Webflow default. */
.rec .padding-section-small {
  padding-top: 8rem;
  padding-bottom: 8rem;
}

.rec__head {
  margin-bottom: 3rem;
}

/* Matches .pfc__eyebrow (index.html:591) so the section reads as part of
   the same system as the case cards. The skill's pill-badge treatment is
   dropped on purpose: an existing house style beats a generic rule.
   One deviation — .pfc__eyebrow hardcodes #222222, which is near-invisible
   on the dark theme. Same visual weight here, via the theme variable. */
.rec__eyebrow {
  margin: 0 0 .7rem;
  font-size: 0.625rem;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--text-color--text-primary);
  opacity: 0.7;
  line-height: 1.4;
}

.rec__title {
  margin: 0;
  font-family: "Fraunces", Georgia, "Times New Roman", serif;
  font-weight: 300;
  font-size: clamp(1.9rem, 3vw, 3rem);
  line-height: 1.1;
  letter-spacing: -0.025em;
  color: var(--text-color--text-primary);
}

/* ── viewport ── */
.rec__marquee {
  position: relative;
  overflow: hidden;
  /* Break out of the 1.25rem page padding so cards run edge to edge. */
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  padding: 1rem 0;
  /* Feather both ends so cards enter and leave instead of getting chopped. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--rec-fade), #000 calc(100% - var(--rec-fade)), transparent);
  mask-image: linear-gradient(to right, transparent, #000 var(--rec-fade), #000 calc(100% - var(--rec-fade)), transparent);
}

.rec__track {
  display: flex;
  gap: var(--rec-gap);
  width: max-content;
  will-change: transform;
  /* `linear` is deliberate here and is the one place the skill's
     "no linear timing" rule does not apply: a marquee is continuous
     transport, not a state change. Any eased curve makes the loop
     visibly accelerate and decelerate once per cycle. */
  animation: rec-scroll var(--rec-duration) linear infinite;
}

.rec__group {
  display: flex;
  gap: var(--rec-gap);
  flex: 0 0 auto;
}

/* The track holds two identical groups. Shifting by exactly one group
   plus one gap puts group 2 where group 1 was, so the loop is seamless.
   Track width = 2W + G, so -50% is short by half a gap — hence the -G/2. */
@keyframes rec-scroll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(calc(-50% - var(--rec-gap) / 2), 0, 0); }
}

/* Pause on hover so a card can actually be read, and on focus-within so
   keyboard users don't have content sliding out from under them. */
.rec__marquee:hover .rec__track,
.rec__marquee:focus-within .rec__track {
  animation-play-state: paused;
}

/* ── card: double-bezel (outer shell + inner core) ── */
.rec-card {
  flex: 0 0 var(--rec-card-w);
  width: var(--rec-card-w);
  min-width: 0;
  box-sizing: border-box;
  padding: var(--rec-shell-pad);
  border-radius: var(--rec-shell-radius);
  background: rgba(20, 20, 20, 0.035);
  /* Hairline ring, not a 1px gray border. */
  box-shadow:
    0 0 0 1px rgba(20, 20, 20, 0.05),
    0 2px 4px -2px rgba(20, 20, 20, 0.04),
    0 16px 32px -24px rgba(20, 20, 20, 0.18);
  transition:
    transform 0.7s var(--rec-ease),
    box-shadow 0.7s var(--rec-ease);
}

.rec-card__core {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 1.5rem;
  height: 100%;
  box-sizing: border-box;
  padding: 1.6rem 1.5rem 1.4rem;
  border-radius: var(--rec-core-radius);
  background: var(--background-color--background-primary);
  /* Inner top highlight — the "machined" edge that sells the nesting. */
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.6);
}

/* Lift on hover. Only transform and box-shadow — no layout properties. */
.rec__marquee:hover .rec-card:hover {
  transform: translate3d(0, -4px, 0);
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--base-color-brand--blue) 35%, transparent),
    0 4px 8px -4px rgba(20, 20, 20, 0.06),
    0 24px 48px -28px rgba(20, 20, 20, 0.28);
}

.rec-card__quote {
  /* Reset the site-wide blockquote rule (terry-mota.webflow.css:193), which
     hardcodes a #e2e2e2 left border that can't follow the theme. Replaced
     below with a hairline accent that reads in both light and dark. */
  margin: 0;
  border-left: 1px solid color-mix(in srgb, var(--base-color-brand--blue) 30%, transparent);
  padding: 0 0 0 0.9rem;
  font-size: 0.8125rem;
  font-weight: 300;
  line-height: 1.7;
  color: var(--text-color--text-primary);
  /* Clamp rather than scroll: every card keeps the same height even if
     one recommendation runs long. Excerpts in the JSON are trimmed to
     roughly 180 characters so this rarely has to engage. */
  display: -webkit-box;
  -webkit-line-clamp: 7;
  line-clamp: 7;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.rec-card__author {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
  padding-top: 1.1rem;
  border-top: 1px solid rgba(20, 20, 20, 0.06);
  /* The site centres every <figcaption> (terry-mota.webflow.css:206 and
     webflow.css:249) and that inherits into the name/role block. Flush
     left, so the text lines up with the avatar and the quote above. */
  text-align: left;
  margin-top: 0;
}

.rec-card__avatar {
  flex: 0 0 2.375rem;
  width: 2.375rem;
  height: 2.375rem;
  border-radius: 50%;
  object-fit: cover;
  background: color-mix(in srgb, var(--base-color-brand--blue) 12%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--base-color-brand--blue) 18%, transparent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--base-color-brand--blue);
  text-transform: uppercase;
  user-select: none;
}

.rec-card__meta { min-width: 0; }

.rec-card__name {
  margin: 0;
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--text-color--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rec-card__role {
  margin: 0.2rem 0 0;
  font-size: 0.625rem;
  font-weight: 300;
  letter-spacing: 0.02em;
  line-height: 1.4;
  color: var(--text-color--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── placeholder guard ── */
/* Visible while data/recommendations.json still has `placeholder: true`,
   so dummy testimonials can't quietly reach production. */
.rec-card__flag {
  align-self: flex-start;
  padding: 0.2rem 0.5rem;
  border-radius: 0.3rem;
  background: #ff9800;
  color: #1a1a1a;
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* ── dark mode ── */
.dark-mode .rec-card {
  background: rgba(255, 255, 255, 0.04);
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.08),
    0 16px 32px -24px rgba(0, 0, 0, 0.6);
}

.dark-mode .rec-card__core {
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.06);
}

.dark-mode .rec-card__author {
  border-top-color: rgba(255, 255, 255, 0.08);
}

.dark-mode .rec__marquee:hover .rec-card:hover {
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--base-color-brand--blue) 45%, transparent),
    0 24px 48px -28px rgba(0, 0, 0, 0.75);
}

/* ── scroll entry ── */
/* Nothing appears statically: head and marquee fade up on a heavy curve,
   staggered, driven by IntersectionObserver in js/recommendations.js.

   The hidden state is deliberately gated behind .reveal-armed, which JS
   adds only once it has an observer ready. Default (no JS, dead JS, old
   browser) is fully visible — an animation must never be able to make
   real content disappear.

   Note: no blur on the marquee — it's a large, wide surface and blurring
   it would force expensive GPU repaints on mobile. */
.rec.reveal-armed .rec__head,
.rec.reveal-armed .rec__marquee {
  opacity: 0;
  transform: translate3d(0, 3rem, 0);
  transition:
    opacity 0.9s var(--rec-ease),
    transform 0.9s var(--rec-ease),
    filter 0.9s var(--rec-ease);
}

.rec.reveal-armed .rec__head { filter: blur(6px); }

.rec.reveal-armed.is-in .rec__head,
.rec.reveal-armed.is-in .rec__marquee {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  filter: blur(0);
}

.rec.reveal-armed.is-in .rec__marquee { transition-delay: 0.12s; }

/* ── responsive ── */
@media (max-width: 767px) {
  .rec {
    --rec-card-w: 19rem;
    --rec-fade: 3rem;
    --rec-duration: 48s;
  }
  .rec .padding-section-small {
    padding-top: 4.5rem;
    padding-bottom: 4.5rem;
  }
  .rec__head { margin-bottom: 2rem; }
  .rec-card__core { padding: 1.35rem 1.25rem 1.2rem; }
}

/* ── reduced motion ── */
/* Stop the loop entirely and hand control back to the user as a normal
   swipeable row, matching how the rest of the site degrades. */
@media (prefers-reduced-motion: reduce) {
  .rec__track {
    animation: none;
  }
  .rec__marquee {
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    padding-left: 1.25rem;
    padding-right: 1.25rem;
    scroll-padding-left: 1.25rem;
  }
  .rec__marquee::-webkit-scrollbar { display: none; }
  .rec-card { scroll-snap-align: start; }
  /* The second group only exists to make the loop seamless. With the loop
     off it would just show every recommendation twice. */
  .rec__group[aria-hidden="true"] { display: none; }
  /* No fade-up, no lift — show everything in place. */
  .rec__head,
  .rec__marquee {
    opacity: 1;
    transform: none;
    filter: none;
    transition: none;
  }
  .rec-card,
  .rec__marquee:hover .rec-card:hover {
    transform: none;
    transition: none;
  }
}
