/* ==========================================================================
   BLVCKOS™ testimonial wall

   Pattern taken from the bottom testimonials section on founderos.com
   (Patrick's reference, 09-09-26). That section is a testimonial.to "Wall of
   Love" widget in large-card mode; this is the same shape rebuilt as our own
   markup on the BLVCKOS palette, so there is no third-party embed on the
   critical path (Build Lesson: no third-party iframes on a landing page).

   What the reference does, and what this reproduces:
     - a MASONRY wall, not a grid and not a marquee: cards keep their natural
       height and the columns fill independently, so a two-line quote and a
       twelve-line quote can sit side by side without either being padded
     - card head = circular avatar, name, role or company on a second line
     - a five-star row in the brand accent under the head
     - an optional bold headline above the quote
     - the quote itself, truncated with a "Show more" toggle when it is long
     - the date, quiet, at the foot of the card
     - VIDEO testimonials mixed into the same flow: full-bleed poster, centre
       play button, name and role over the bottom-left, stars bottom-right
     - a "Load more" button, so the section opens short and expands

   Differences from the reference, all deliberate:
     - dark, on the BLVCKOS palette, accent teal #4ab897 not lime
     - our own markup, no embed, no external script, no cookies
     - the wall renders from a data array, so it is empty until real
       graduates exist. It must never be seeded with invented people.
   ========================================================================== */

/* The wall breaks out of the copy column. Every other section on this page
   is set to --media-w (611px on a 1440 screen) because long copy needs a
   short measure, but a masonry wall at that width gives ~195px columns and
   the card head wraps to three lines. The reference runs its wall far wider
   than its body copy for the same reason. */
.tw {
  width: min(1240px, 92vw);
  max-width: 100vw;
  margin: clamp(24px, 2.8vw, 38px) auto 0;
}

/* Masonry. Same mechanism the results cards use, and here it is the RIGHT
   mechanism rather than the wrong one: testimonials genuinely vary in length,
   and a wall where every card is forced to the same height either truncates
   the long ones or pads the short ones with dead space. Ragged is what makes
   a wall of real quotes look real. The reference site does exactly this.

   The equal-height variant below exists so that judgement can be looked at
   rather than argued about. */
.tw-wall {
  columns: 1;
  column-gap: 14px;
}

/* Equal-height alternative: every card in a row takes the tallest height, and
   grid-auto-rows makes every row match too, so all nine are identical. The
   cost is visible: short quotes float in a lot of empty card. */
.tw.l-grid .tw-wall {
  columns: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  grid-auto-rows: 1fr;
  gap: 14px;
}

.tw.l-grid .tw-card {
  margin-bottom: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
}

/* Push the date to the foot of a stretched card instead of leaving it
   floating under the quote with a gap beneath it. */
.tw.l-grid .tw-date { margin-top: auto; padding-top: 12px; }

.tw-card {
  break-inside: avoid;
  /* 14 -> 22 (14-09) -> 38.5 (17-09). Jay again: the wall is "still too
     bunched". He opened at 50 percent more and settled on 75, so this is
     22 x 1.75 exactly rather than a round number chosen nearby.

     The COLUMN gap is deliberately left at 14. That is the horizontal gap
     between the two columns at 641 and up, and Jay was looking at a phone,
     which is one column, so the only gap he can have meant is this one.
     Raising both would also narrow the columns themselves. */
  margin-bottom: 38.5px;
  background: #0a0a0b;
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 14px;
  padding: 20px 20px 17px;
  overflow: hidden;
}

/* ---- head: avatar, name, role ---- */
/* THE STARS SIT ON THE NAME LINE, HARD RIGHT (Patrick, 15-09-26).

   They used to be a sibling BELOW this header, which cost a whole row of card
   height for five glyphs. space-between pushes them to the right edge while
   the avatar and name stay left; min-width:0 on the id block is what lets a
   long name wrap instead of shoving the stars off the card. */
.tw-head {
  display: flex;
  align-items: center;
  gap: 11px;
  margin-bottom: 12px;
}

.tw-head .tw-stars {
  margin-left: auto;
  margin-bottom: 0;
  flex: 0 0 auto;
}

.tw-ava {
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  object-fit: cover;
  background: #17171a;
  border: 1px solid rgba(255, 255, 255, 0.09);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sans);
  font-weight: 700;
  font-size: 13px;
  color: var(--ink-soft);
}

.tw-id {
  min-width: 0;
}

.tw-name {
  font-family: var(--sans);
  font-weight: 700;
  font-size: 14.5px;
  line-height: 1.3;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.tw-role {
  font-family: var(--sans);
  font-weight: 400;
  font-size: 12.5px;
  line-height: 1.35;
  color: var(--ink-dim);
  margin-top: 1px;
}

/* ---- stars ----------------------------------------------------------------
   Gold, not teal. This is the one deliberate exception to "teal is the only
   colour on the page", and it earns it: a five-star row is a convention the
   eye decodes before it reads anything, and a teal star reads as decoration
   rather than as a rating. It also sits far enough from the accent in hue
   that it cannot be confused with the CTA, so the button keeps its job.

   Tokenised so the whole wall recolours from one line.
   --------------------------------------------------------------------------- */
.tw {
  --tw-star: #f5c451;
  --tw-star-glow: rgba(245, 196, 81, 0.42);
}

/* Lime, matching the reference site more literally. Warmer brands tend to
   want the gold; this is here so the choice can be seen rather than argued. */
.tw.s-lime {
  --tw-star: #c9f24e;
  --tw-star-glow: rgba(201, 242, 78, 0.40);
}

/* Teal, the old behaviour, kept only to compare against. */
.tw.s-teal {
  --tw-star: #4ab897;
  --tw-star-glow: rgba(74, 184, 151, 0.45);
}

.tw-stars {
  display: flex;
  gap: 3px;
  margin-bottom: 11px;
}

.tw-stars svg {
  width: 15px;
  height: 15px;
  fill: var(--tw-star);
  filter: drop-shadow(0 0 5px var(--tw-star-glow));
}

/* ---- body ---- */
.tw-title {
  font-family: var(--sans);
  font-weight: 700;
  font-size: 15.5px;
  line-height: 1.4;
  color: var(--ink);
  margin-bottom: 9px;
}

.tw-quote {
  /* 14 (Patrick, 15-09-26). */
  font-size: 14px;
  line-height: 1.62;
  color: var(--ink-soft);
}

.tw-quote p + p {
  margin-top: 11px;
}

/* Collapsed state. The reference clamps long quotes and offers "Show more";
   the height is generous enough that most quotes never need it. */
/* THE CARD'S HEIGHT CAP (Patrick, 15-09-26: Brendan A's card is the maximum,
   anything taller gets shrunk, with Show more landing about one line after his
   last line). Cards go from 161-325 tall to 161-214.

   MAX-HEIGHT IS THE LOAD-BEARING RULE, not the line-clamp. -webkit-line-clamp
   is unreliable on a box full of block-level <p> children, and the proof is
   Caleb O: at the old value his quote measured scrollHeight 203 against
   clientHeight 203, so the script read "no overflow", stripped the clamp and
   gave him no button. His card stayed 296 tall while every other long one
   capped correctly. A max-height cannot be ignored that way, and the script
   detects overflow against it properly.

   The line-clamp stays alongside it purely for the ellipsis, which max-height
   alone does not give.

   102px is BRENDAN A'S OWN QUOTE HEIGHT, measured, and that is the whole point:
   his card is the reference, so the cap has to be the exact height his quote
   already occupies or he gets clamped by it. At 92px he was, picking up a Show
   more button on a quote that was already complete, which is the opposite of
   using him as the reference.

   It is four lines plus the gap between his two paragraphs. Every longer quote
   now cuts at the same place his ends, and the tallest card lands on a 1.77
   ratio, which is 16:9 to two decimal places. Jay asked for 16:9 in the same
   review. */
.tw-quote.is-clamped {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  line-clamp: 4;
  -webkit-box-orient: vertical;
  max-height: 102px;
  overflow: hidden;
}

.tw-more {
  appearance: none;
  -webkit-appearance: none;
  margin-top: 7px;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--sans);
  /* 13 (Patrick, 15-09-26). */
  font-size: 13px;
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

/* HOVER GATED BEHIND A REAL POINTER (Patrick, 15-09-26: "when you click Show
   more it turns green for a millisecond, I do not want it to do that").

   A touch screen has no hover, so it SYNTHESISES one on tap and leaves it
   applied until the next touch elsewhere. Any :hover colour therefore reads as
   a flash on every tap, which is what he is seeing. The media query means a
   phone never matches the rule at all, while a mouse still gets the highlight.

   Nothing else changes: the button still toggles open and closed. */
@media (hover: hover) and (pointer: fine) {
  .tw-more:hover {
    color: var(--teal);
  }
}

.tw-date {
  margin-top: 14px;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.04em;
  color: var(--ink-dim);
}

/* ---- video card ---- */
/* ---- the ledger on a card ----
   Added 15-09-26 for Jay's instruction that the last testimonial carries
   Adrian's performance ledger as a 16:9 rectangle.

   The image is pure #000 on a #0a0a0b card, so it would otherwise dissolve
   into the card with no edge at all. The hairline is what makes it read as a
   screenshot of a screen rather than as a hole. 8px radius, not the card's
   14: a smaller inner radius is what stops a nested rounded box looking like
   it is bulging out of its container.

   aspect-ratio as well as the width/height attributes, so the card reserves
   the exact box before the image arrives and the wall does not reflow under
   the reader.

   The margin turned over when Patrick moved the image below the quote on
   15-09-26: the 14px is now ABOVE it, which is the same step .tw-date uses to
   come off the quote, so the two things that can sit at the bottom of a card
   leave the same gap. Nothing below it, because the card's own 17px bottom
   padding closes the box. */
/* NO OUTLINE, 17-09-26. Jay, 16-09, on the ledger screenshots: "it'll just
   look a lot cleaner without that." Patrick scoped it to the testimonial
   screenshots only, so the house divider under the Day 1 / Day 30 lockup in
   section 3 is untouched.

   What this gives up, stated because it was deliberate when it went in: the
   image is pure #000 on a #0a0a0b card, so with no hairline it has no edge at
   all and reads as part of the card rather than as a screenshot of a screen.
   That is exactly the "cleaner" Jay asked for, and the grid's own glow is what
   now tells you where the picture starts.

   The radius goes with it: an 8px corner on an invisible box rounds the
   corners of a black image against a black card, which nothing can see, and it
   would clip the grid's outer glow for no reason. */
.tw-ledger {
  margin: 14px 0 0;
  background: #000;
}

.tw-ledger img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
}

.tw-card.is-video {
  padding: 0;
  border-color: rgba(255, 255, 255, 0.09);
}

.tw-vid[data-video=""] {
  cursor: default;
}

.tw-vid {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(74, 184, 151, 0.07), transparent 62%),
    #0d0d0f;
  cursor: pointer;
  border: 0;
  padding: 0;
  overflow: hidden;
}

.tw-vid img,
.tw-vid video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.tw-vid::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.78) 0%, rgba(0, 0, 0, 0.15) 34%, transparent 60%);
  pointer-events: none;
}

.tw-play {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 58px;
  height: 58px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.22);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  border: 1px solid rgba(255, 255, 255, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
  transition: transform 0.2s ease, background 0.2s ease;
}

.tw-vid:hover .tw-play {
  transform: translate(-50%, -50%) scale(1.07);
  background: rgba(255, 255, 255, 0.32);
}

.tw-play::before {
  content: "";
  width: 0;
  height: 0;
  margin-left: 4px;
  border-left: 16px solid #fff;
  border-top: 10px solid transparent;
  border-bottom: 10px solid transparent;
}

.tw-vmeta {
  position: absolute;
  left: 15px;
  right: 15px;
  bottom: 13px;
  z-index: 2;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 10px;
  text-align: left;
}

.tw-vmeta .tw-name,
.tw-vmeta .tw-role {
  color: #fff;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.7);
}

.tw-vmeta .tw-role {
  color: rgba(255, 255, 255, 0.82);
}

.tw-vmeta .tw-stars {
  margin: 0 0 2px;
  flex: 0 0 auto;
}

.tw-vmeta .tw-stars svg {
  width: 13px;
  height: 13px;
}

/* ---- load more ---- */
.tw-foot {
  margin-top: 6px;
  text-align: center;
}

.tw-load {
  appearance: none;
  -webkit-appearance: none;
  min-height: 46px;
  padding: 13px 30px;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.01em;
  color: var(--ink);
  background: #000;
  border: 1px solid rgba(74, 184, 151, 0.45);
  border-radius: 10px;
  cursor: pointer;
  transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.18s ease;
}

.tw-load:hover {
  border-color: var(--teal);
  box-shadow: 0 0 18px -6px rgba(74, 184, 151, 0.7);
  transform: translateY(-1px);
}

/* --------------------------------------------------------------------------
   Empty state. This is what ships until real graduates exist, and it is the
   whole reason the wall is data-driven: the section has to be able to say
   "nothing here yet" rather than be filled with invented people.
   -------------------------------------------------------------------------- */
.tw-empty {
  padding: clamp(30px, 4vw, 52px) 22px;
  border: 1px dashed rgba(255, 255, 255, 0.16);
  border-radius: 14px;
  background: #08080a;
  text-align: center;
}

.tw-empty .tw-empty-mark {
  font-family: var(--sans);
  font-weight: 800;
  font-size: 40px;
  line-height: 1;
  color: rgba(255, 255, 255, 0.09);
  margin-bottom: 14px;
}

.tw-empty p {
  max-width: 34em;
  margin: 0 auto;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.05em;
  line-height: 1.75;
  color: var(--ink-dim);
}

/* --------------------------------------------------------------------------
   Sample banner. Only ever rendered on the local preview page, never on a
   real landing page, so a screenshot of the design can never be mistaken for
   real testimonials.
   -------------------------------------------------------------------------- */
.tw-sample {
  margin-bottom: 18px;
  padding: 11px 14px;
  border: 1px solid rgba(255, 180, 60, 0.42);
  background: rgba(255, 180, 60, 0.08);
  border-radius: 8px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.07em;
  line-height: 1.7;
  color: #ffd48a;
  text-align: center;
}

.tw-card.is-sample {
  position: relative;
}

.tw-card.is-sample::before {
  content: "SAMPLE";
  position: absolute;
  right: 0;
  top: 0;
  z-index: 3;
  padding: 3px 8px 2px;
  font-family: var(--mono);
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: #ffd48a;
  background: rgba(40, 28, 8, 0.94);
  border-left: 1px solid rgba(255, 180, 60, 0.42);
  border-bottom: 1px solid rgba(255, 180, 60, 0.42);
  border-radius: 0 13px 0 6px;
}

/* --------------------------------------------------------------------------
   Columns. The reference runs two on desktop; three suits our narrower cards
   on a wide screen and keeps the wall from becoming a tall pair of ribbons.
   -------------------------------------------------------------------------- */
/* 641, not 640: every other breakpoint in this repo switches at 641 (the
   phone/desktop canvas boundary in styles.css). At 640 exactly, the wall went
   two-column while the rest of the page was still on the phone canvas. */
@media (min-width: 641px) {
  .tw-wall {
    columns: 2;
  }
}

@media (min-width: 1100px) {
  .tw-wall {
    columns: 3;
  }
}

@media (prefers-reduced-motion: reduce) {
  .tw-load,
  .tw-play {
    transition: none;
  }
}


/* --------------------------------------------------------------------------
   PHONE TYPE, BACK UP AGAIN. This block was the testimonial half of Patrick's
   15-09-26 instruction that everything at 14 on a phone becomes 13; on
   17-09-26 he reversed it, so it now pins the phone back to 14. Lives here
   rather than in styles.css so it sits with the component it changes, and
   testimonials.css loads second so it wins at equal specificity.
   -------------------------------------------------------------------------- */
@media (max-width: 640px) {
  /* Leading is a ratio, 1.62, so it follows the size on its own. */
  .tw-quote {
    font-size: 14px;
  }

  /* THE HEIGHT CAP MOVES WITH THE TYPE, or the rule stops meaning what it was
     set to mean. 102px is Brendan A's own quote height at 14px, which is 4.5
     lines of a 22.68px line box; 95 was that same 4.5 lines at 13. The type is
     back at 14 so the cap goes back to 102, and the cards hold the shape they
     were signed off in. */
  .tw-quote.is-clamped {
    max-height: 102px;
  }

  .tw-load {
    font-size: 14px;
  }

  /* Show more was 13 at EVERY width, so it is the one thing in this pass that
     was not part of the 15-09 step-down: Jay set it on 14-09, 13.5 -> 13, in
     the same breath as the 16:9 cards. It moves here because Patrick's
     instruction was every 13 on the phone, and it is phone-only, so desktop
     keeps Jay's 13. One line to put back if he wants it. */
  .tw-more {
    font-size: 14px;
  }
}
