/* ==========================================================================
   MEDIA
   ==========================================================================
   Everything that carries a picture instead of a paragraph: the transparent
   animations, the title cards, the film strip and the video embeds.

   THE ANIMATIONS ARE TRANSPARENT PNG-ALPHA GIFS, drawn to sit over gameplay
   footage. On a web page there is no footage under them, so each one gets a
   PLATE: a slightly raised panel with the page's own dot field showing
   through. Without it a transparent gif on a dark page looks like a broken
   image with text floating in it.

   They are also FLAT - the gif pass drops every shadow and glow, because gif
   alpha is one bit. So the plate supplies the depth the clip cannot: the
   elevation, the inner highlight, the ring. The clip supplies the motion.
   ========================================================================== */

/* ---------------------------------------------------------------- a clip -- */

.clip {
  position: relative;
  /* A COLUMN, so the caption can be pushed to the bottom and every tile in a
     row ends up the same height with its caption on the same line. */
  display: flex; flex-direction: column;
  border-radius: var(--r-lg, 18px);
  border: 1px solid var(--line);
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(224,168,60,.07), transparent 62%),
    linear-gradient(180deg, var(--surf), var(--bg-lift));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05), 0 18px 44px rgba(0,0,0,.45);
  overflow: hidden;
  isolation: isolate;
}

/* the dot field, under the clip and inside the plate */
.clip::before {
  content: '';
  position: absolute; inset: 0; z-index: 0;
  background-image: radial-gradient(rgba(255,255,255,.16) 1px, transparent 1px);
  background-size: 22px 22px;
  -webkit-mask-image: radial-gradient(ellipse 74% 66% at 50% 34%, #000 6%, transparent 78%);
          mask-image: radial-gradient(ellipse 74% 66% at 50% 34%, #000 6%, transparent 78%);
  opacity: .55;
}

/* THE MEDIA BOX IS A FIXED SHAPE, and this is the alignment fix.
   `height: auto` let every clip follow its own aspect ratio - these run from
   2.11:1 to 1.3:1 - so the caption bar landed at a different height in every
   tile and a row of four looked broken. A 16:9 box with `contain` gives every
   tile an identical media area; the animations letterbox inside it, which is
   invisible because they are transparent and the plate shows through. */
.clip > img {
  position: relative; z-index: 1;
  display: block;
  width: 100%; height: auto;
  aspect-ratio: 16 / 9;
  object-fit: contain;
}

/* A caption UNDER the clip, not over it. The animations already carry their own
   gold caption burned into the last frame; a second line of gold text on top of
   that is two captions arguing. This one is grey and it names the mechanic. */
.clip-cap {
  position: relative; z-index: 2;
  margin-top: auto;          /* pinned to the bottom of the tile */
  display: flex; align-items: baseline; gap: 10px;
  padding: 14px 18px 16px;
  border-top: 1px solid var(--line);
  background: rgba(0,0,0,.28);
}
.clip-cap b {
  font-size: 11px; font-weight: 800; letter-spacing: .2em;
  text-transform: uppercase; color: var(--gold);
  flex: none;
}
.clip-cap span { font-size: 14px; color: var(--tx-3); line-height: 1.45; }

/* ------------------------------------------------------------ a clip grid -- */

/* TWO COLUMNS, EQUAL HEIGHT.

   This used to be `align-items: start` because the clips have different aspect
   ratios and stretching them left a band of empty plate under the shorter ones.
   That was treating the symptom: the real fix is the fixed 16:9 media box above,
   and with that in place stretch is correct. With the media box fixed above, equal-height tiles are
   now correct rather than wasteful - every caption sits on the same line. */
.clipgrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
  gap: 22px;
}
.clipgrid .wide { grid-column: 1 / -1; }
@media (max-width: 760px) {
  .clipgrid { grid-template-columns: 1fr; }
  .clipgrid .wide { grid-column: auto; }
}

/* --------------------------------------------------------- the showcase --- */
/* One clip, one paragraph, alternating sides. This is the shape most of the
   product page is in now: the picture states it, the text qualifies it. */

.showcase {
  display: grid;
  grid-template-columns: 1.15fr .85fr;
  gap: 48px;
  align-items: center;
}
.showcase.flip > .clip { order: 2; }
.showcase h3 {
  font-size: clamp(22px, 2.4vw, 30px);
  letter-spacing: -.02em; margin-bottom: 14px;
}
.showcase p { color: var(--tx-2); line-height: 1.65; }
.showcase p + p { margin-top: 12px; }
@media (max-width: 900px) {
  .showcase { grid-template-columns: 1fr; gap: 26px; }
  .showcase.flip > .clip { order: 0; }
}

/* Vertical rhythm for a column of showcases. Bigger than the gap inside one,
   so a reader can tell where a block ends without a rule between them. */
.stack { display: flex; flex-direction: column; gap: clamp(56px, 7vw, 104px); }

/* -------------------------------------------------------- the film strip --- */
/* A horizontal scroller of every animation. It is a scroller rather than a
   grid because there are fifteen of them and a grid of fifteen moving images
   is a page nobody can read. */

.strip {
  display: flex; gap: 16px;
  overflow-x: auto;
  padding: 4px 0 18px;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: var(--line-2) transparent;
}
.strip::-webkit-scrollbar { height: 8px; }
.strip::-webkit-scrollbar-track { background: transparent; }
.strip::-webkit-scrollbar-thumb {
  background: var(--line-2); border-radius: 99px;
}
.strip > .clip {
  flex: 0 0 clamp(260px, 32vw, 380px);
  scroll-snap-align: start;
}

/* ------------------------------------------------------- the title cards --- */
/* The ten 1920x1080 cards. They are already whole compositions, so they get no
   plate and no caption - just a frame and a shadow, laid out 16:9. */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 18px;
}
.shot {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--r, 12px);
  border: 1px solid var(--line);
  overflow: hidden;
  background: var(--bg-sink);
  box-shadow: 0 14px 34px rgba(0,0,0,.5);
  transition: transform .3s var(--ease, cubic-bezier(.16,1,.3,1)),
              border-color .3s ease;
}
.shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.shot:hover { transform: translateY(-3px); border-color: var(--line-2); }

/* ============================================================= the video ===
   A CLICK-TO-LOAD FACADE, and the reason is a standing rule: this site makes
   ZERO external requests until somebody asks it to.

   A YouTube <iframe> in the markup is about half a megabyte from four Google
   domains, on every visit, whether or not anybody watches - and it sets
   cookies before a single frame plays. So what ships is a poster (one of our
   own title cards, already on this domain), a play button, and a click handler
   in site.js that swaps in the real iframe with autoplay.

   Nothing external is touched until the click. See mountTube() in site.js.
   ========================================================================= */

.tube {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-lg, 18px);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--bg-sink);
  box-shadow: 0 24px 60px rgba(0,0,0,.55);
  cursor: pointer;
}
.tube img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .6s var(--ease, cubic-bezier(.16,1,.3,1)), filter .4s ease;
}
.tube:hover img { transform: scale(1.02); filter: brightness(1.06); }
.tube iframe { width: 100%; height: 100%; border: 0; display: block; }

/* THE VEIL, so a bright frame cannot swallow the button - and a band along
   the bottom, so it cannot swallow the label either. The trailer's poster
   carries its own wordmark and a "powered by" line in exactly the corner
   .tube-label sits in, and the two were printing over each other. The band
   pushes whatever the poster has down into the dark and gives the label
   something to sit on, on any frame. */
.tube::after {
  content: '';
  position: absolute; inset: 0;
  background:
    linear-gradient(to top, rgba(0,0,0,.96) 0%, rgba(0,0,0,.9) 14%,
                            rgba(0,0,0,.62) 27%, transparent 44%),
    radial-gradient(60% 60% at 50% 50%, rgba(0,0,0,.16), rgba(0,0,0,.6));
  pointer-events: none;
  transition: opacity .4s ease;
}
.tube.playing::after { display: none; }

.tube-play {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 92px; height: 92px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(224,168,60,.5);
  background: rgba(10,11,13,.6);
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
  box-shadow: 0 0 0 0 rgba(224,168,60,.35);
  transition: transform .3s var(--snap, cubic-bezier(.22,1.16,.36,1)),
              background .3s ease, box-shadow .3s ease;
  z-index: 2;
}
.tube-play svg { width: 30px; height: 30px; fill: var(--gold-lit); margin-left: 4px; }
.tube:hover .tube-play {
  transform: translate(-50%, -50%) scale(1.08);
  background: rgba(224,168,60,.16);
  box-shadow: 0 0 0 14px rgba(224,168,60,.07);
}

/* what it is, bottom left, so the poster is not the only label */
/* ONE LINE, ON THE FLOOR OF THE TILE. It was a two-line stack, which put the
   eyebrow about a quarter of the way up the frame - and that is exactly where
   the trailer's own poster carries its "powered by" line, so the two printed
   over each other. Side by side at the bottom edge, the label sits in the
   darkest part of the veil and clears whatever the poster has above it,
   whichever poster that turns out to be. */
.tube-label {
  position: absolute; left: 22px; right: 22px; bottom: 16px; z-index: 2;
  display: flex; align-items: baseline; gap: 11px;
  text-align: left;
}
.tube-label b {
  flex: none;
  font-size: 11px; font-weight: 800; letter-spacing: .22em;
  text-transform: uppercase; color: var(--gold);
  text-shadow: 0 2px 10px rgba(0,0,0,.9);
}
.tube-label span {
  min-width: 0;
  font-size: clamp(15px, 1.6vw, 19px); font-weight: 700; color: var(--tx);
  text-shadow: 0 2px 14px rgba(0,0,0,.85);
}
/* Narrow enough and one line stops fitting; go back to stacking rather than
   letting the title wrap under the eyebrow's hanging indent. */
@media (max-width: 520px) {
  .tube-label { flex-direction: column; align-items: flex-start; gap: 4px; }
}
.tube.playing .tube-play,
.tube.playing .tube-label { display: none; }

/* a note under the embed, for the placeholder state */
.tube-note {
  margin-top: 12px;
  font-size: 13px; color: var(--tx-4);
}
.tube-note code {
  font-family: var(--mono); font-size: 12px;
  color: var(--gold-dim);
  background: rgba(255,255,255,.04);
  padding: 2px 7px; border-radius: 5px;
}

/* two side by side on a wide screen */
.tubes {
  display: grid; grid-template-columns: 1fr 1fr; gap: 26px;
}
@media (max-width: 900px) { .tubes { grid-template-columns: 1fr; } }

/* ---------------------------------------------------------- the banner ----- */
/* The 2400px marketing banner, used as a section opener. It is already a
   finished composition with the logo and the pills in it, so it gets a frame
   and nothing else. */

.banner {
  display: block; width: 100%; height: auto;
  border-radius: var(--r-lg, 18px);
  border: 1px solid var(--line);
  box-shadow: 0 22px 56px rgba(0,0,0,.5);
}


/* ============================================================ screenshots ===
   REAL IN-GAME CAPTURES, and until they exist the same slots hold a placeholder
   that says what belongs in each one - see shot() in graphics/render.py. The
   layout is finished either way, so dropping a capture in changes a picture and
   not a page.

   16:9 throughout, because that is what a screenshot is. A gallery that lets
   each image set its own height reads as a scrapbook.
-------------------------------------------------------------------------- */

.shots {
  display: grid; grid-template-columns: 1fr 1fr; gap: 20px;
}
.shots .wide { grid-column: 1 / -1; }
@media (max-width: 820px) {
  .shots { grid-template-columns: 1fr; }
  .shots .wide { grid-column: auto; }
}

.shot-fig { margin: 0; display: flex; flex-direction: column; }

/* EVERY CAPTURE KEEPS ITS OWN SHAPE.
   This used to force `aspect-ratio: 16/9` with `object-fit: cover`, which was
   right while all four were 1920x1080 placeholders and wrong the moment real
   ones arrived: a 21:9 ultrawide grab of the table builder got its left third
   cropped off, and the left third is the panel the shot exists to show.
   Nothing here knows the ratio now - the file does. */
.shot-fig img {
  display: block; width: 100%; height: auto;
  border-radius: var(--r-lg, 18px);
  border: 1px solid var(--line);
  background: var(--bg-sink);
  box-shadow: 0 18px 46px rgba(0,0,0,.5);
}
.shots { align-items: stretch; }
/* `margin-top: auto` rather than 12px, with the gap moved to padding: two
   captures side by side no longer have the same height, so the captions are
   pushed to the bottom of their column and the row still lines up. Same trick
   as .clip-cap. */
/* TOP-ALIGNED, NOT BOTTOM. `margin-top: auto` pushed every caption to the
   bottom of its cell, which was the right call while the four captures were
   different heights and the alternative was a ragged row. They are all 16:9
   now, so the images end level and it is the captions that have to start
   level - bottom-aligning them made a one-line caption sit twenty pixels
   below the two-line one beside it, under two pictures that ended together. */
.shot-fig figcaption {
  padding-top: 12px;
  font-size: 14px; color: var(--tx-3); line-height: 1.5;
}
.shot-fig figcaption b { color: var(--tx-2); font-weight: 700; }

/* A MARKETING GRAPHIC AS A SECTION RULE. The nine numbered bars are already
   drawn with the site's tokens, so using one as a divider costs nothing and
   ties the page to the forum post and the storefront. */
.divider {
  display: block; width: 100%; height: auto;
  margin: 0 0 clamp(30px, 4vw, 52px);
  border-radius: var(--r, 12px);
  opacity: .92;
}


/* ============================================================== the timeline ===
   REPLACES A HORIZONTAL SCROLLER that did not work. Twelve clips in an
   overflow-x row meant nine of them were off-screen with nothing indicating
   they existed, the tiles were a different height from each other, and on a
   trackpad the whole page tried to scroll sideways with it.

   A hand of Dead Man's Deal is a SEQUENCE, so it reads as one: a brass rail
   down the left, a numbered node per beat, and the clip beside it. Nothing is
   hidden, the order is the point, and every row aligns to the same two columns.
-------------------------------------------------------------------------- */

.tl { position: relative; display: flex; flex-direction: column; gap: 0; }

/* the rail, behind the nodes */
.tl::before {
  content: '';
  position: absolute; left: 23px; top: 12px; bottom: 12px; width: 2px;
  background: linear-gradient(180deg, transparent, var(--gold-dim) 8%,
              var(--gold-dim) 92%, transparent);
  opacity: .5;
}
@media (max-width: 760px) { .tl::before { left: 17px; } }

.tl-row {
  position: relative;
  display: grid;
  grid-template-columns: 48px 1fr;
  gap: clamp(18px, 3vw, 34px);
  padding: clamp(16px, 2.4vw, 26px) 0;
  align-items: start;
}
@media (max-width: 760px) { .tl-row { grid-template-columns: 36px 1fr; gap: 16px; } }

/* the node */
.tl-n {
  position: relative; z-index: 1;
  width: 48px; height: 48px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 15px; font-weight: 800;
  color: var(--gold);
  background: var(--bg);
  border: 1px solid rgba(224,168,60,.4);
  box-shadow: 0 0 0 6px var(--bg);
  transition: border-color .3s ease, color .3s ease, transform .3s ease;
}
@media (max-width: 760px) { .tl-n { width: 36px; height: 36px; font-size: 13px; } }
.tl-row:hover .tl-n {
  border-color: var(--gold); color: var(--gold-lit); transform: scale(1.06);
}

/* the body: words on the left, clip on the right */
.tl-body {
  display: grid; grid-template-columns: 1fr 1.15fr;
  gap: clamp(20px, 3vw, 40px);
  align-items: center;
}
@media (max-width: 900px) { .tl-body { grid-template-columns: 1fr; gap: 16px; } }

.tl-body h3 {
  font-size: clamp(19px, 2vw, 24px);
  letter-spacing: -.02em; margin-bottom: 9px;
}
.tl-body p { color: var(--tx-3); font-size: 15px; line-height: 1.6; }
.tl-body .clip { margin: 0; }

/* the last beat is the one that ends a match, so it gets the red */
.tl-row.fatal .tl-n { border-color: rgba(214,58,52,.5); color: #ef5a52; }
.tl-row.fatal:hover .tl-n { border-color: #d63a34; }

/* ================================================================== the CTA ===
   A band, not a button. It appears three times on the product page and twice on
   the home page, and each one restates the price and the licence - because a
   reader who has just been convinced by a section should not have to scroll to
   find out what it costs.
-------------------------------------------------------------------------- */

/* ---------------------------------------------------------------- modes ----
   The two ways to run it, side by side. Two cards rather than a paragraph
   because the point is that they are ALTERNATIVES - a reader deciding whether
   this fits their map wants to see both shapes at once, not read to the end of
   one to find out the other exists.
---------------------------------------------------------------------------- */

.modes {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(16px, 2.2vw, 26px);
  align-items: start;
  margin-top: clamp(26px, 4vw, 40px);
}
@media (max-width: 760px) { .modes { grid-template-columns: 1fr; } }

.mode { padding: clamp(22px, 2.8vw, 32px); text-align: left; }
.mode h3 {
  margin: 14px 0 10px; font-size: clamp(19px, 2.2vw, 24px); font-weight: 800;
  letter-spacing: -.02em; color: var(--tx);
}
.mode p { font-size: 14.5px; line-height: 1.62; color: var(--tx-3); }
.mode code { font-size: .92em; }

.mode-tag {
  display: inline-block; padding: 5px 11px; border-radius: 999px;
  font-size: 11.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--tx-3); border: 1px solid var(--line-2); background: rgba(255,255,255,.03);
}
.mode-tag.alt { color: var(--gold); border-color: rgba(224,168,60,.36);
  background: rgba(224,168,60,.08); }

.mode .ticks { margin-top: 16px; }

.mode-note {
  max-width: 74ch; margin: clamp(20px, 3vw, 28px) auto 0;
  font-size: 14px; line-height: 1.65; color: var(--tx-3); text-align: center;
}

.ctaband {
  display: flex; align-items: center; justify-content: space-between;
  gap: clamp(18px, 3vw, 40px); flex-wrap: wrap;
  margin-top: clamp(34px, 5vw, 60px);
  padding: clamp(22px, 3vw, 34px) clamp(24px, 3.4vw, 40px);
  border-radius: var(--r-lg, 18px);
  border: 1px solid rgba(224,168,60,.26);
  background:
    radial-gradient(120% 160% at 12% 0%, rgba(224,168,60,.13), transparent 62%),
    linear-gradient(180deg, var(--surf), var(--bg-lift));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05), 0 20px 48px rgba(0,0,0,.4);
}
.ctaband-txt { min-width: 0; }
.ctaband-txt b {
  display: block; font-size: clamp(18px, 2vw, 23px); font-weight: 800;
  color: var(--tx); letter-spacing: -.02em;
}
.ctaband-txt span {
  display: block; margin-top: 6px; font-size: 14.5px; color: var(--tx-3);
}
.ctaband .cta { margin: 0; flex: none; }
@media (max-width: 620px) {
  .ctaband { flex-direction: column; align-items: flex-start; }
}

/* A TILE WITH NO VIDEO BEHIND IT IS NOT A BUTTON. site.js marks unset tubes
   aria-disabled and appends "coming soon" to the label; the play triangle was
   the one thing still saying "press me", which is the whole inconsistency. */
.tube[aria-disabled="true"] .tube-play { display: none; }
.tube[aria-disabled="true"]:hover img { transform: none; filter: none; }


/* ======================================================== THE ROUND CLIP ====
   A recording of the game running, sitting above the two videos. It is a
   <video> rather than an <img> for the reasons in the markup; everything here
   is about making it read as a piece of footage rather than a player, since
   there is nothing to press. */
.roundclip {
  margin: 0 0 clamp(20px, 3vw, 30px);
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--bg-sink);
  box-shadow: 0 24px 60px rgba(0,0,0,.42);
}
.roundclip video {
  display: block;
  width: 100%; height: auto;
  /* The clip's own shape. Not 16:9 and not cropped to it - see the markup. */
  aspect-ratio: 1600 / 670;
  background: var(--bg-sink);
}
.roundclip figcaption {
  padding: 10px 14px;
  font-size: 12.5px; color: var(--tx-4);
  border-top: 1px solid var(--line);
  background: rgba(0,0,0,.25);
}
