/* ============================================
   SOCIAL POST — reusable case component
   ============================================
   A minimal, abstracted social-post frame. Not a replica of any
   platform UI: same layout language (avatar, handle, media, dots,
   action row) drawn in the site's own type and stroke weight.

   Reusable across cases. Everything skinnable is a variable, so a
   case restyles it by redeclaring these on its own body class:

     .case.jord .socialPost { --postFg:#DCDDCD; --postBg:#1D1D1D; }

   Markup is produced by /inc_social_post.php.
   Behaviour (auto-advance, video in-view play) by /dist/js/social-post.js.
   ============================================ */

.socialPost {
  --postW:            300px;
  --postBg:           rgba(255,255,255,.04);
  --postFg:           currentColor;
  --postMuted:        rgba(127,127,127,.75);
  --postChrome:       rgba(127,127,127,.28);
  --postRadius:       14px;
  --postMediaRatio:   4 / 5;
  --postGap:          .75rem;

  width: 100%;
  max-width: var(--postW);
  margin: 0 auto;
  color: var(--postFg);
  background: var(--postBg);
  border: 1px solid var(--postChrome);
  border-radius: var(--postRadius);
  overflow: hidden;
  font-family: var(--mono, monospace);
  text-align: left;
  container-type: inline-size;
}

/* --- header ------------------------------------------------ */

.socialPost__head {
  display: flex;
  align-items: center;
  gap: var(--postGap);
  padding: .7rem .8rem;
}

.socialPost__avatar {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid var(--postChrome);
  object-fit: cover;
  display: grid;
  place-items: center;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: 0;
  line-height: 1;
  text-transform: uppercase;
  color: var(--postMuted);
  overflow: hidden;
}

.socialPost__ident { flex: 1 1 auto; min-width: 0; }

.socialPost__handle {
  display: flex;
  align-items: center;
  gap: .35rem;
  font-size: .78rem;
  font-weight: 700;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.socialPost__music {
  display: flex;
  align-items: center;
  gap: .3rem;
  font-size: .66rem;
  line-height: 1.4;
  color: var(--postMuted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.socialPost__menu { flex: 0 0 auto; opacity: .7; }

/* --- media -------------------------------------------------- */

.socialPost__media {
  position: relative;
  aspect-ratio: var(--postMediaRatio);
  background: rgba(0,0,0,.25);
  overflow: hidden;
}

.socialPost--video { --postMediaRatio: 9 / 16; }

.socialPost__track {
  display: flex;
  height: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.socialPost__track::-webkit-scrollbar { display: none; }

.socialPost__slide {
  flex: 0 0 100%;
  height: 100%;
  scroll-snap-align: center;
  scroll-snap-stop: always;
}

.socialPost__media img,
.socialPost__media video {
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  margin: 0;
}

/* A landscape frame among portrait slides letterboxes rather than
   crops, so a wide shot is never beheaded. */
.socialPost__slide--contain img { object-fit: contain; }

.socialPost__count {
  position: absolute;
  top: .6rem;
  right: .6rem;
  font-size: .62rem;
  letter-spacing: .5px;
  padding: .18rem .45rem;
  border-radius: 999px;
  background: rgba(0,0,0,.45);
  color: #fff;
  backdrop-filter: blur(3px);
}

.socialPost__mute {
  position: absolute;
  right: .6rem;
  bottom: .6rem;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: rgba(0,0,0,.45);
  color: #fff;
  cursor: inherit;
  backdrop-filter: blur(3px);
}

/* --- dots ---------------------------------------------------- */

.socialPost__dots {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: .3rem;
  padding: .6rem 0 .1rem;
}

/* The action row is normally last. When a case turns it off, the frame
   ends at the media or the dots and needs that padding back. */
.socialPost > .socialPost__media:last-child,
.socialPost > .socialPost__dots:last-child { margin-bottom: .55rem; }

.socialPost__dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--postChrome);
  transition: background .25s ease, transform .25s ease;
}

.socialPost__dot.is-active {
  background: var(--postFg);
  transform: scale(1.15);
}

/* --- actions -------------------------------------------------- */

.socialPost__actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: .55rem .8rem .8rem;
}

.socialPost__action {
  display: flex;
  align-items: center;
  gap: .3rem;
  font-size: .7rem;
  line-height: 1;
  color: var(--postFg);
}

.socialPost__action--end { margin-left: auto; gap: 0; }

.socialPost__icon {
  width: 17px;
  height: 17px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.socialPost__caption {
  padding: 0 .8rem .9rem;
  font-size: .7rem;
  line-height: 1.55;
  color: var(--postMuted);
}

/* --- the row of posts ------------------------------------------ */

.socialRow {
  display: grid;
  grid-template-columns: repeat(var(--postCols, auto-fit), minmax(240px, 1fr));
  gap: 2rem 1.5rem;
  width: 100%;
  margin: 2rem auto 0;
  align-items: start;
}

/* A fixed column count is a desktop instruction. Narrower than that and
   the count has to give, or the frames become postage stamps. */
@media (max-width: 1024px) {
  .socialRow { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .socialRow { grid-template-columns: 1fr; }
}

/* --- empty slot (asset not produced yet) ----------------------- */

.socialPost--empty .socialPost__media {
  display: grid;
  place-items: center;
  border-top: 1px dashed var(--postChrome);
  border-bottom: 1px dashed var(--postChrome);
}
.socialPost--empty .socialPost__media p {
  font-size: .68rem;
  color: var(--postMuted);
  text-align: center;
  padding: 1rem;
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .socialPost__track { scroll-behavior: auto; }
}

/* ============================================
   MEDIA GRID — reusable case component
   ============================================
   Rows of muted, in-view autoplaying video tiles. Row widths come from
   the pattern passed to nofont_video_grid(); --cols is set per row and
   --tileRatio per grid, so the block carries no hard-coded counts.

   Markup: /inc_media_grid.php   Behaviour: /dist/js/social-post.js
   ============================================ */

.videoGrid,
.mediaGrid {
  --tileRatio: 3 / 2;
  --tileGap:   1.25rem;
  --tileRadius: 4px;

  display: flex;
  flex-direction: column;
  gap: var(--tileGap);
  width: 100%;
  margin: 2rem auto 0;
}

.videoGrid__row,
.mediaGrid__row {
  display: grid;
  grid-template-columns: repeat(var(--cols, 1), 1fr);
  gap: var(--tileGap);
}

.videoTile,
.mediaTile {
  display: flex;
  flex-direction: column;
  margin: 0;
}

.videoTile video,
.mediaTile video,
.mediaTile img {
  display: block;
  width: 100%;
  max-width: none;
  aspect-ratio: var(--tileRatio);
  object-fit: cover;
  margin: 0;
  border-radius: var(--tileRadius);
  background: rgba(0,0,0,.3);
}

.mediaGrid--contain video,
.mediaGrid--contain img { object-fit: contain; }

/* Keep the top of a tall image when it is cropped. A page screenshot is
   recognisable from its head, not its middle. */
.mediaGrid--top video,
.mediaGrid--top img { object-position: top; }

/* Full-page screenshots are all different lengths. Fixing them to one
   ratio would crop four of five, so let each keep its own height and
   hang the row from the top. */
.mediaGrid--natural .mediaGrid__row { align-items: start; }
.mediaGrid--natural video,
.mediaGrid--natural img { aspect-ratio: auto; height: auto; object-fit: fill; }

/* Site screens sit on a dark page and would otherwise bleed into it. */
.mediaGrid--bordered video,
.mediaGrid--bordered img { border: 1px solid var(--postChrome, rgba(127,127,127,.28)); }

/* Dissolve the foot of each tile. The mask takes the image and its border
   together, so the grid fades into whatever is behind it rather than into a
   colour we would have to keep in sync with the section — and the bottom
   edge of the row stops being a hard line across the page, which softens
   the hand-off to the next section. */
.mediaGrid--fade video,
.mediaGrid--fade img {
  -webkit-mask-image: linear-gradient(to bottom,
                      #000 0%, #000 52%, rgba(0,0,0,.55) 78%, transparent 100%);
          mask-image: linear-gradient(to bottom,
                      #000 0%, #000 52%, rgba(0,0,0,.55) 78%, transparent 100%);
}

/* A tile with no asset yet: the shape of what is coming. */
.mediaTile--empty > div {
  display: grid;
  place-items: center;
  aspect-ratio: var(--tileRatio, 3 / 2);
  border: 1px dashed var(--postChrome, rgba(127,127,127,.35));
  border-radius: var(--tileRadius);
  font-family: var(--mono, monospace);
  font-size: .68rem;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--postMuted, currentColor);
  text-align: center;
  padding: 1rem;
}

/* Caption sits under its tile, in the page's own voice rather than
   overlaid on the picture — or above it, which is what a faded tile
   wants, since a caption under a dissolving image reads as detached. */
.mediaGrid--capTop .mediaTile figcaption { padding: 0; }

.videoTile figcaption,
.mediaTile figcaption {
  /* padding: .7rem 0 0; */
  margin: 7px 0 7px 0;
  font-family: var(--mono, monospace);
  font-size: .72rem;
  letter-spacing: .3px;
  line-height: 1.45;
  color: var(--postMuted, currentColor);
  text-align: left;
}

/* A row keeps its full count on desktop and never wraps. On a tablet it
   collapses to two, on a phone to one — the tiles crop rather than shrink,
   because these are there to give a feel for the work, not to be read. */
@media (max-width: 1024px) {
  /* Two columns, but the rows keep their identity so the desktop rhythm
     survives the breakpoint. A three-up row leads with one full-width tile and
     then a pair; a lone tile fills the width rather than sitting half-width
     with a hole beside it. Jord's 2-3-1 therefore reads 2-1-2-1 and Ryde's 3-3
     reads 1-2-1-2, instead of every row orphaning its last tile. */
  .videoGrid__row,
  .mediaGrid__row { grid-template-columns: repeat(2, 1fr); }
  .videoGrid__row > :first-child:nth-last-child(3),
  .mediaGrid__row > :first-child:nth-last-child(3),
  .videoGrid__row > :only-child,
  .mediaGrid__row > :only-child { grid-column: 1 / -1; }
  .videoGrid,
  .mediaGrid { --tileGap: 1rem; }
  .mediaGrid:not(.mediaGrid--natural) video,
  .mediaGrid:not(.mediaGrid--natural) img {
    aspect-ratio: var(--tileRatioMd, var(--tileRatio));
  }
}

@media (max-width: 560px) {
  .videoGrid__row,
  .mediaGrid__row { grid-template-columns: 1fr; }
  .videoGrid,
  .mediaGrid { --tileGap: .75rem; }
  .mediaGrid:not(.mediaGrid--natural) video,
  .mediaGrid:not(.mediaGrid--natural) img {
    aspect-ratio: var(--tileRatioSm, var(--tileRatioMd, var(--tileRatio)));
  }
}

/* ============================================
   HERO VIDEO
   ============================================
   Full-width film, muted autoplay, with a real unmute control.
   Markup: /inc_media_grid.php   Behaviour: /dist/js/social-post.js
   ============================================ */

.heroVideo {
  width: 100%;
  margin: 0;
}

/* The control anchors to the film, not to the figure — otherwise the
   caption's height pushes it off the bottom of the picture. */
.heroVideo__frame {
  position: relative;
  line-height: 0;
}

.heroVideo video {
  display: block;
  width: 100%;
  max-width: none;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  margin: 0;
  background: rgba(0,0,0,.4);
  border-radius: var(--tileRadius, 4px);
}

/* The control is a button, not a badge — it has to be reachable by
   keyboard, and it has to say which state it is in. */
.heroVideo__sound {
  position: absolute;
  right: 1rem;
  bottom: 1rem;
  display: flex;
  align-items: center;
  gap: .45rem;
  padding: .5rem .8rem .5rem .65rem;
  border: 1px solid rgba(255,255,255,.28);
  border-radius: 999px;
  background: rgba(0,0,0,.45);
  backdrop-filter: blur(4px);
  color: #fff;
  font-family: var(--mono, monospace);
  font-size: .68rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  cursor: inherit;
  transition: background .2s ease, border-color .2s ease;
}

.heroVideo__sound:hover,
.heroVideo__sound:focus-visible {
  background: rgba(0,0,0,.68);
  border-color: rgba(255,255,255,.5);
}

.heroVideo__icon {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.heroVideo figcaption {
  padding: .7rem 0 0;
  font-family: var(--mono, monospace);
  font-size: .72rem;
  letter-spacing: .3px;
  color: var(--postMuted, currentColor);
  text-align: left;
}

@media (max-width: 560px) {
  .heroVideo__sound { right: .6rem; bottom: .6rem; padding: .4rem .6rem .4rem .5rem; }
  .heroVideo__sound span { display: none; }
}

/* ============================================
   COLLAGE
   ============================================
   Irregular scatter, built from three cheap devices rather than
   absolute positioning: staggered column starts, per-column insets
   so items differ in width, and alternate items hanging off the
   opposite edge. Nothing overlaps, so it reflows honestly.

   Markup: /inc_media_grid.php
   ============================================ */

.collage {
  display: grid;
  grid-template-columns: repeat(var(--collageCols, 3), 1fr);
  gap: var(--collageGap, 2rem);
  align-items: start;
  width: 100%;
  margin: 0 auto;
}

.collage__col {
  display: flex;
  flex-direction: column;
  gap: var(--collageGap, 2rem);
  margin-top: var(--off, 0);
}

.collage__item {
  margin: 0;
  width: calc(100% - var(--inset, 0px));
}

/* Alternate items hang off the far edge of their column. This is what
   stops three tidy stacks from reading as three tidy stacks. */
.collage__col .collage__item:nth-child(even) { margin-left: auto; }

.collage__item img,
.collage__item video {
  display: block;
  width: 100%;
  max-width: none;
  height: auto;
  margin: 0;
  border-radius: 2px;
  box-shadow: 0 18px 40px rgba(0,0,0,.45);
}

.collage__item figcaption {
  padding: .6rem 0 0;
  font-family: var(--mono, monospace);
  font-size: .68rem;
  letter-spacing: .3px;
  color: var(--postMuted, currentColor);
  text-align: left;
}

.collage__item--empty > div {
  display: grid;
  place-items: center;
  aspect-ratio: var(--tileRatio, 1440 / 2039);
  border: 1px dashed rgba(255,255,255,.3);
  border-radius: 2px;
  font-family: var(--mono, monospace);
  font-size: .66rem;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: rgba(255,255,255,.45);
  text-align: center;
  padding: 1rem;
}

/* Two columns on a tablet, one on a phone. The stagger halves and then
   goes, because an offset column at phone width is just a gap. */
@media (max-width: 1024px) {
  .collage { grid-template-columns: repeat(min(var(--collageCols, 3), 2), 1fr); }
  .collage__col { margin-top: calc(var(--off, 0) / 2); }
  .collage__item { width: 100%; }
}

@media (max-width: 560px) {
  .collage { grid-template-columns: 1fr; gap: 1.25rem; }
  .collage__col { margin-top: 0; gap: 1.25rem; }
  .collage__col .collage__item:nth-child(even) { margin-left: 0; }
}

/* ============================================
   ROOM SECTION — full-bleed photographic ground
   ============================================
   The Nansen .u-bgColor mechanism, used properly: a photograph behind a
   whole section. No scrim — type sits straight on the picture, so the
   backgrounds themselves have to be dark and quiet enough to carry it.
   ============================================ */

.roomSection {
  position: relative;

  /* .mainContent is width:80%, and .u-bgColor only lifts its max-width — so
     a coloured section is still inset. A photographic ground has to run to
     the window edge or it reads as a big picture rather than as the page
     changing. The content inside is re-constrained below. */
  width: 100%;
  max-width: 100%;
  padding-bottom: 6rem;
}

/* Two classes, not one. base.css sets background-size:80% on
   .mainContent.u-bgColor — Nansen's Richardo tile, which every full-bleed
   section on the site inherits. That selector is (0,2,0); a bare
   .roomSection is (0,1,0) and loses, so `cover` was silently ignored and
   the photograph painted at 80% width, centred, looking inset.
   Matching the markup's own pair wins on source order. */
.mainContent.roomSection {
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;

  /* Scrolls with the content and covers the whole section — the full width
     of the page and the collage behind it. cover on a very tall box crops
     the picture to a vertical slice and upscales it, so these grounds want
     to be exported tall rather than wide. */
  background-attachment: scroll;
}

/* Full-bleed ground, but the copy still has to line up with every other
   section on the page rather than spreading with the window. */
.roomSection .contentGrid {
  max-width: 1440px;
  margin-left: auto;
  margin-right: auto;
}

.roomSection .caseItem {
  /* .caseItem carries width:100% from base.css, so percentage padding here
     is added to a box that is already full width — that was 60px of
     horizontal overflow at 1500px. Constrain the width instead of padding
     the inside of it. 1150px is ~20% under the 1440 the rest of the page
     uses, which sits the collage in from the edges. */
  width: 92%;
  max-width: 1150px;
  margin-left: auto;
  margin-right: auto;
  padding: 0;
}

.roomSection__mark {
  display: block;
  margin: 0 auto 1.5rem;
  height: 46px;
  width: auto;
}
