/* ─────────────────────────────────────────────────────────────
   Card deck — ported from web-showcase-recreation-code
   (app/globals.css + app/page.tsx). The card sizing, depths, border radius,
   shadow and side controls are the source's own values.

   The source's four slot transforms and its promote/eject keyframes are NOT
   here. They now live in tavvlo-showcase.js, which evaluates them against the
   scroll position every frame instead of playing them as animations — that is
   what lets the deck reverse the instant the scroll does. A `transition` or
   `animation` on the cards would fight those per-frame writes, so the cards
   carry no CSS motion at all.

   Two further adaptations from the source:
     - the source is a full-screen app, so .stack-stage is position:absolute
       inset:0 against the viewport. Here it pins inside a track that owns the
       scroll length.
     - the per-card colour drove the whole page background. It is scoped to
       this section so it cannot repaint the rest of the page.

   Everything is namespaced under .tv-sc so nothing collides with the
   Webflow stylesheet.
   ───────────────────────────────────────────────────────────── */

.tv-sc {
  --black: #202124;
  --card-w: min(31vw, 43vh);
  --card-h: calc(var(--card-w) * 1.6);
  /* These two are what the section paints BEFORE the script runs, so they must
     match card 1 or a reload shows the wrong colour first. They used to hold
     the stock template's blue, which the .45s transition below then cross-faded
     to black on every refresh. Keep them in step with card 1 in data-cards. */
  --page-color: #0a0a0a;
  --ctl: #f2f2f0;

  position: relative;
  /* clip, not hidden: `hidden` would make this a scroll container and the
     sticky stage would pin against it instead of the viewport. `clip` still
     stops the thrown card from pushing a horizontal scrollbar. */
  overflow-x: clip;
  overflow-y: visible;
  background: var(--page-color);
  transition: background-color .45s ease;
}

/* One band of scroll per throw, plus a screen to hold the stage. */
.tv-sc .sc-track { height: calc(100vh + 70vh * var(--card-count, 6)); }

.tv-sc .stack-stage {
  position: sticky;
  top: 0;
  z-index: 6;
  height: 100vh;
  min-height: 560px;
  /* NOT touch-action:none. The stage covers the whole screen while pinned, so
     blocking touch here means a phone cannot swipe past the deck at all. */
  touch-action: pan-y;
}

.tv-sc .deck-perspective {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--card-w);
  height: var(--card-h);
  perspective: 1100px;
  transform: translate(-50%, -50%);
  transform-style: preserve-3d;
}
.tv-sc .deck {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
}

.tv-sc .showcase-card {
  position: absolute;
  inset: 0;
  overflow: hidden;
  margin: 0;
  border: 2px solid var(--black);
  border-radius: calc(var(--card-w) * 0.095);
  background: #fff;
  box-shadow: 0 24px 30px rgba(25, 26, 29, .22);
  /* the card keeps facing the camera past 90deg of rotateY as it is thrown */
  backface-visibility: visible;
  transform-origin: 50% 50%;
  transform-style: preserve-3d;
  will-change: transform;
}
.tv-sc .showcase-card::after {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .18);
  content: "";
  pointer-events: none;
}

.tv-sc .card-face,
.tv-sc .card-face img,
.tv-sc .card-face video { display: block; width: 100%; height: 100%; }
.tv-sc .card-face { overflow: hidden; background: #fff; }
.tv-sc .card-face img,
.tv-sc .card-face video { object-fit: cover; pointer-events: none; }
.tv-sc .card-face--full img { object-position: center; }

.tv-sc .card-face--split { display: flex; flex-direction: column; }
.tv-sc .card-face--split .card-title-art { height: auto; flex: none; }
.tv-sc .card-face--split .card-video-wrap { flex: 1 1 auto; min-height: 0; }

/* Live-text title panel, for cards that ship no baked headline art.
   Proportions are measured off the original 1308x836 PNGs and expressed as a
   share of card width, so a text card sits where an image card sits: same pill
   position, same left margin, same headline size.

   The panel takes its height from its own content rather than a fixed ratio.
   The baked art was a fixed 1308x836 because it was a picture; text is not, and
   letting the panel breathe means a longer or shorter headline shifts the video
   below it instead of overflowing the card.

   The panel itself carries no padding: container units resolve against a
   container's CONTENT box, so padding here would shrink the reference every cqw
   below is measured from. .ctt-inner does the insetting instead. */
.tv-sc .card-face--split .card-title-text {
  flex: none;
  height: auto;
  background: #fff;
  container-type: inline-size;
}
.tv-sc .card-title-text .ctt-inner {
  box-sizing: border-box;
  /* pill top 112px, left 120px of 1308. The gap below the headline is a
     variable because it is the only lever for where the animation sits: the
     panel is content-height, so a four-line headline pushes the video down a
     whole line. A card can claw some of that back with `titleGap`. */
  padding: 8.56% 5% var(--title-gap, 7.5%) 9.2%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.tv-sc .card-title-text .pill {
  background: var(--pill, #ffbb25);
  color: #202124;
  border-radius: 999px;
  padding: 1.45cqw 2.9cqw;
  font: 600 3.06cqw/1 Geist, Inter, system-ui, sans-serif;
  white-space: nowrap;
}
.tv-sc .card-title-text h3 {
  margin: 7.38cqw 0 0;
  color: #202124;
  /* A shade under the PNG's 10.9%: Geist sets wider than the Product Sans the
     original art was drawn in, and this keeps the headline to three lines. */
  font: 700 9.8cqw/1.14 Geist, Inter, system-ui, sans-serif;
  letter-spacing: -.02em;
  text-wrap: balance;
}

/* One highlighted word in a headline, in the Tavvlo green used site-wide
   (Webflow's --color-palette--primary). */
.tv-sc .card-title-text h3 .hl { color: var(--color-palette--primary, #88ef56); }

/* ── Side labels ──────────────────────────────────────────────────────
   These were the source's clickable "Next card" / "Show me!" controls. They
   are inert labels now — the two halves of "Complaints Management" flanking
   the deck — so the pointer affordances are gone and only the rule under each
   word is kept. Colour still follows --ctl so they stay readable when the
   card behind them is black. */
.tv-sc .side-label {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 18;
  display: flex;
  width: calc(50% - var(--card-w) * .5);
  flex-direction: column;
  justify-content: center;
  padding: 0 20px;
  color: var(--ctl);
  font-family: Geist, "Google Sans", Inter, sans-serif;
  font-size: clamp(28px, 2.64vw, 36px);
  font-weight: 700;
  line-height: 1.2;
  pointer-events: none;
  user-select: none;
}
.tv-sc .side-label span {
  position: relative;
  display: block;
  opacity: .5;
}
.tv-sc .side-label span::after {
  position: absolute;
  top: calc(100% + 9px);
  width: 150px;
  height: 3px;
  background: currentColor;
  opacity: .4;
  content: "";
}
.tv-sc .side-label--left  { left: 0;  align-items: flex-end;   padding-right: min(10vw, 138px); text-align: right; }
.tv-sc .side-label--left span::after  { right: -20px; }
.tv-sc .side-label--right { right: 0; align-items: flex-start; padding-left: min(10vw, 138px);  text-align: left; }
.tv-sc .side-label--right span::after { left: -20px; }

@media (max-width: 900px) {
  .tv-sc { --card-w: min(64vw, 38vh); }   /* height comes from .sc-track */
  .tv-sc .stack-stage { min-height: 480px; }

  /* Phone reads it as one centred title above the deck rather than a pair
     flanking it: each half takes half the width and they meet at the middle,
     so "Complaints Management" runs across the centre of the page. */
  .tv-sc .side-label {
    top: 0;
    bottom: auto;
    width: 50%;
    justify-content: flex-start;
    padding: 5.5vh 0 0;
    font-size: 20px;
  }
  .tv-sc .side-label span::after { width: 100%; left: 0; right: auto; }
  .tv-sc .side-label--left  { align-items: flex-end;   padding-right: .28em; }
  .tv-sc .side-label--right { align-items: flex-start; padding-left: .28em; }
}

/* The script snaps the throw to its endpoints under reduced motion, so cards
   change without flying. Only the background fade needs muting here. */
@media (prefers-reduced-motion: reduce) {
  .tv-sc { transition: none; }
}
