/* ==========================================================================
   THE COLLAB TILE, ONE LOOK, TWO PAGES.

   The homepage hero strip and the /work grid print the SAME element, built by
   the same function in collabTiles.js. This file is the other half of that:
   the frame, the scrim, the caption and the play mark, so the two surfaces
   cannot come to look different the way they could not come to say different
   things. Each page keeps ONLY the rule that sets a tile's width.

   Load it AFTER tokens.css: --font-display comes from there.

   Nothing here is scoped to a page or a section, deliberately. A rule written
   as `.hero .tile-cap` would have to be written twice.
   ========================================================================== */

/* THE FRAME.
   9:16 because every published row is a Reel or a TikTok. The cover fills it
   and is CROPPED rather than squashed: Instagram and TikTok covers arrive in
   several shapes and a stretched one reads as a mistake. The background is
   what shows for the fraction of a second before a cover paints, and behind
   the named fallback for a row that has no cover at all. */
/* THE PHONE CAPTION BUDGET, IN ONE PLACE AND AS ONE DECISION.
   ==========================================================================
   The caption is at its tallest when the business name takes TWO lines and
   the foot row takes two, and the foot wraps before it truncates on purpose
   (a creator's handle is her name, and "@khalanieatsi..." beside a full view
   count is the wrong thing to give up). At the phone tile's 156px that came
   to 40.19% of the tile against the 40% ceiling the caption is held to.

   IT WAS INVISIBLE UNTIL 2026-09-10 for a reason worth writing down: the hero
   strip showed the top SIX published rows, and none of the six had a name
   long enough to reach the maximum. The strip carries every published row
   now, so the worst case is a thing that happens rather than a thing that
   might.

   THE THREE VALUES ARE ONE DECISION, NOT THREE, and that is why they are
   here rather than each beside the rule that uses it. No one of them is worth
   more than about two pixels; what buys the headroom is their sum, and a
   break harness aimed at any single one reports GREEN because the other two
   still cover it. Together they take the worst case from 40.2% to 38.2%.

   THE TOP PADDING IS NOT IN THIS BUDGET. It is the fade zone the scrim
   contrast assertion measures against -- move it and the caption text rides
   up into the transparent end of the gradient -- so the bottom padding is
   what gives. */
.collab-tile {
  --cap-pad-bottom: 0.35rem;
  --cap-biz-line: 1.14;
  --cap-foot-top: 0.1rem;
  aspect-ratio: 9 / 16;
  border-radius: 14px;
  border: 1px solid rgba(232,224,208,0.14);
  background:
    linear-gradient(160deg, rgba(232,224,208,0.13) 0%, rgba(232,224,208,0.04) 55%, rgba(23,46,42,0.35) 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(232,224,208,0.28);
  text-decoration: none;
  overflow: hidden;
  position: relative;
}
.collab-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* THE CAPTION CARRIES THE SAME FOUR FACTS THE TILE'S aria-label SPEAKS.
   ==========================================================================
   The strip used to be photographs and nothing else, and the business name,
   the city, the creator and the view count lived only in a grid most of the
   way down the homepage. A visitor who never scrolled that far saw pretty
   pictures of somewhere, by someone, doing well by some measure.

   A SCRIM, NOT A PANEL. The photograph is the reason the tile is there, so
   the caption is laid over a gradient that fades to nothing well before the
   top of the tile rather than over a solid block that would take a third of
   the picture. The text-shadow is the second line: a gradient stop is a fixed
   amount of darkening and a photograph is not a fixed amount of bright, so
   the shadow holds the letters against a blown-out sky the scrim alone would
   not cover. */
.tile-cap {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 1.55rem 0.55rem var(--cap-pad-bottom);
  box-sizing: border-box;
  /* THE STOPS ARE PLACED SO THE FADE HAPPENS ABOVE THE TEXT, NOT THROUGH IT.
     The top padding is the fade zone and the text begins where it ends, so
     every stop below about 72% has to be dark enough on its own: white on a
     scrim of alpha a over a BLOWN-OUT WHITE photograph clears the 4.5:1 floor
     at a >= 0.56, and nothing under a photograph can be assumed darker than
     white. An earlier distribution faded to 0.31 where the business name sits
     and measured 2.31:1 against the brightest pixel behind it on the phone,
     which is why the numbers below are what they are: measured, on the real
     published covers, at 390 and at 1440. */
  background: linear-gradient(to top,
    rgba(8,16,14,0.95) 0%,
    rgba(8,16,14,0.91) 50%,
    rgba(8,16,14,0.82) 72%,
    rgba(8,16,14,0.38) 88%,
    rgba(8,16,14,0) 100%);
  pointer-events: none;
  text-shadow: 0 1px 3px rgba(0,0,0,0.65);
}
.tile-cap-biz {
  font-family: var(--font-display);
  color: #FFFFFF;
  font-size: 0.76rem;
  font-weight: 500;
  line-height: var(--cap-biz-line);
  /* Two lines, then an ellipsis. A restaurant with a long name must not push
     the city and the handle off the bottom of the tile. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.tile-cap-place {
  color: rgba(255,255,255,0.82);
  font-size: 0.62rem;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tile-cap-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  /* IT WRAPS BEFORE IT TRUNCATES. A creator's handle is her name, and
     "@khalanieatsi…" beside a full view count is the wrong thing to give up:
     on a narrow tile the count drops to its own line and both stay whole. The
     ellipsis below is the last resort for a handle no tile width would hold. */
  flex-wrap: wrap;
  gap: 0.1rem 0.35rem;
  margin-top: var(--cap-foot-top);
  padding-top: 0.28rem;
  border-top: 1px solid rgba(255,255,255,0.22);
}
.tile-cap-handle {
  color: #FFFFFF;
  font-size: 0.62rem;
  font-weight: 600;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tile-cap-views {
  color: rgba(255,255,255,0.86);
  font-size: 0.6rem;
  letter-spacing: 0.03em;
  white-space: nowrap;
  flex: 0 0 auto;
}
@media (min-width: 769px) {
  .tile-cap { padding: 2.1rem 0.65rem 0.6rem; }
  /* THE DESKTOP TILE KEEPS THE ROOMIER SETTING IT ALWAYS HAD. It is 240px
     wide against the phone's 156, so the same worst-case caption is a much
     smaller share of it and there is nothing to buy back here. The budget
     above is handed back rather than inherited. */
  .collab-tile { --cap-biz-line: 1.22; --cap-foot-top: 0.18rem; }
  .tile-cap-biz { font-size: 0.84rem; }
  .tile-cap-place, .tile-cap-handle { font-size: 0.68rem; }
  .tile-cap-views { font-size: 0.66rem; }
}

/* THE PLAY MARK IS OURS, AND IT IS ON EVERY TILE.
   ==========================================================================
   EVERY PUBLISHED ROW IS A REEL OR A TIKTOK, so every tile is a video and
   every tile says so. Until recently none of them did: the page drew no play
   mark at all, and the one visitors saw on some tiles and not others was
   BURNED INTO THE COVER IMAGE by the platform it came from. Instagram's
   og:image thumbnail for a Reel is rendered with the play triangle already on
   it; TikTok's oEmbed cover is a clean frame. So the mark appeared on exactly
   the Instagram rows, which is a property of where the picture came from and
   not of anything we decided.

   IT IS SIZED IN PERCENT OF THE FRAME, DELIBERATELY. The burned-in Instagram
   triangle is about 8.6% of the cover's width and it scales with the cover,
   so a mark of ours with a fixed pixel cap would cover it on a small strip
   tile and be swamped by it on a wide /work tile. Everything below is a
   fraction of the frame, which makes ours about 12.4% wide and 15% tall
   against their 8.6% and 11%, at EVERY size: theirs falls inside ours,
   centred, and one mark reads on every tile whatever platform it came from.

   IT IS NOT A LINK AND IT IS NOT READ ALOUD. The whole tile is already the
   link and the caption already carries the facts, so this is decoration:
   pointer-events off, aria-hidden on. */
.tile-play {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 25%;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  background: rgba(10,18,16,0.5);
  box-shadow: 0 2px 16px rgba(0,0,0,0.32);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.tile-play svg {
  width: 82%; height: 82%;
  display: block;
  fill: #FFFFFF;
  filter: drop-shadow(0 1px 3px rgba(0,0,0,0.4));
}

/* A TILE WITH NO STORED COVER NAMES THE PLATFORM.
   The anonymous play icon that used to fill this frame made "no cover on
   file" and "the image would not load" look identical and claimed nothing at
   all. The business, the city, the creator and the views are printed by the
   caption over the bottom of this same frame, so the only thing missing is
   what KIND of post it is, and that is what sits here.

   The mark sits in the column with the label rather than on top of it: the
   named node already owns the middle of the frame. Same element, same
   function, one placement rule per context. */
.cover-named {
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 0.45rem; text-align: center;
  width: 100%; height: 100%; padding: 1rem 0.7rem; box-sizing: border-box;
}
.cover-named-platform {
  color: rgba(232,224,208,0.45);
  font-size: 0.6rem; letter-spacing: 0.16em;
  text-transform: uppercase; font-weight: 600;
}
.cover-named .tile-play {
  position: static;
  transform: none;
  width: 13%;
  min-width: 26px;
  max-width: 44px;
  flex: 0 0 auto;
  margin-bottom: 0.15rem;
}
