/* The games hub — the page the Games button lands on.
   Three doors, each one wearing a picture of what is behind it. Sized for the
   same readers as the games themselves: big targets, big type. */

.games-home-page .Master > section.games-home {
  padding: 10px 16px 24px;
}

.games-home-page footer {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 8px;
  padding: 2px 10px;
  margin: 0;
}

.games-home-page .build-tag,
.games-home-page .page-hits {
  font-family: 'Trebuchet MS', 'Segoe UI', sans-serif;
  font-size: var(--footer-size);
  letter-spacing: 0.04em;
  opacity: 0.45;
  margin: 0;
}

.games-home-page .page-hits::before { content: '· '; }

/* ---------------------------------------------------------------- intro */

.games-home__intro { max-width: 46rem; }

.games-home__intro h2 {
  margin: 8px 0 6px;
  color: var(--accent);
  font-size: calc(1.5rem * var(--font-scale, 1));
}

.games-home__intro p {
  margin: 0 0 18px;
  color: var(--ink-muted);
  font-size: calc(1.02rem * var(--font-scale, 1));
  line-height: 1.55;
}

/* The "still being built" notice.

   Scoped to the section, not to the box the note sits in, so it holds
   wherever on the page the note is put. It used to be written
   .games-home__intro .games-home__note, and when the note moved down out of
   the intro the rule quietly stopped matching and the red went with it.

   Still two classes deep rather than one: if the note ever goes back up into
   the intro, .games-home__intro p is 0-1-1 and would otherwise win on
   specificity and put the muted ink back. The width and line height are set
   here because they used to come from the intro, and the note no longer
   lives there. The 30px is the gap a group heading gets above it. */
.games-home .games-home__note {
  max-width: 46rem;
  margin: 30px 0;
  color: var(--danger);
  font-size: calc(1.08rem * var(--font-scale, 1));
  line-height: 1.55;
  font-weight: 700;
  font-style: italic;
}

/* Hazard tape, laid under the words.

   Black and yellow because that is the one colour pair everybody already
   reads as "work going on here" without having to be told, and slanted
   because tape is.

   Fixed colours rather than theme tokens, for the reason looseEnds.css gives
   about its line colours: a derived yellow would stop being a hazard yellow
   the moment a theme's warn colour drifted, and tape that matches the decor
   is not tape.

   Faded with opacity on OPAQUE stripes rather than with transparent colours
   in the gradient, and that is what makes it work in all eight themes with
   no fork. Opacity blends the whole pattern toward whatever is behind it --
   pale yellow against grey on a light page, olive against near-black on a
   dark one -- and because yellow and black start so far apart, 40% of that
   gap is still plainly a stripe either way.

   The tilt is a hair under a degree. Enough that it reads as something laid
   across the page rather than a border, not enough to look like a mistake.
   A transform, so it costs the layout nothing. */
.games-home__note::after {
  content: '';
  display: block;
  height: 14px;
  margin-top: 9px;
  border-radius: 3px;
  background: repeating-linear-gradient(-45deg,
      #F2C400 0 14px,
      #1B1B1B 14px 28px);
  opacity: 0.4;
  transform: rotate(-0.6deg);
}

/* ----------------------------------------------------------------- grid */

/* The group headings.

   Added when the page grew past a dozen doors and "pick one and play" stopped
   being useful advice: with everything in one grid, the card games and a
   half-built engine looked equally finished, and somebody with ten minutes
   found that out by opening five of them. */
.games-home__group {
  margin: 30px 0 2px;
  color: var(--ink);
  font-size: calc(1.22rem * var(--font-scale, 1));
  letter-spacing: 0.01em;
  border-bottom: 2px solid var(--rule);
  padding-bottom: 6px;
}

/* The one at the top of the page needs less air above it than the ones
   further down, because the intro is already right there.

   Written as "the heading under the intro" rather than :first-of-type, which
   is what it used to say. :first-of-type counts within a parent, and the
   Experimental group now has a parent of its own -- so its heading was the
   first h3 in that box, matched, and lost the 30px that separates a group
   from the grid above it. */
.games-home__intro + .games-home__group {
  margin-top: 6px;
}

.games-home__group-note {
  margin: 8px 0 16px;
  max-width: 46rem;
  color: var(--ink-muted);
  font-size: calc(0.98rem * var(--font-scale, 1));
  line-height: 1.5;
}

/* A whole group in one box, so one class can take the lot away.
   The Experimental group is the only one wearing it, and it wears is-admin
   beside it: the permission tree in css/core/main.css hides .is-admin from
   everybody and gives it back to an account holding admin.

   This rule is here because of what the tree hands back. It shows things as
   inline-block, which is right for the nav button it was written for and
   wrong for a block of cards: an auto-fill grid in a shrink-to-fit box works
   out to exactly one column, so the three cards would come back in a stack a
   quarter of the page wide. main.css has the same exception for
   section.only-unknown -- this keeps it to this page rather than adding a
   third one to a stylesheet every page loads.

   Both halves of the tree that can show an .is-admin are named, because
   either one showing it while this rule sat quiet is the broken column
   again. In practice have-admin is always the one: superadmin and support
   are bundles that contain admin, and auth_user_perm expands them before the
   browser ever sees the list. */
body.have-admin .games-home__group-box.is-admin,
body.have-superadmin .games-home__group-box.is-admin {
  display: block;
}

/* auto-FILL, not auto-fit, and the difference is the whole of this comment.

   Both make as many 19rem columns as will fit. They differ only over the
   columns that end up EMPTY: auto-fit collapses them to zero and shares the
   space out among the cards that are left; auto-fill keeps them, so a card is
   always one column wide whatever else is in the grid.

   While every card sat in one grid together there was never an empty column,
   so the two behaved identically and auto-fit was fine. Splitting the page
   into Card Games / Early Versions / Experimental is what exposed it: the
   first group has ONE card in it, auto-fit collapsed the other three columns,
   and Card Games became a single tile 1553 pixels wide and 871 tall -- four
   times the size of every other card on the page.

   With auto-fill a group of one, a group of three and a group of nine all draw
   their cards at exactly the same size, which is the point of a grid of
   cards. The same trap, written up the same way, is in css/games/kingdoms.css
   on the artwork contact sheet. */
.games-home__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  gap: 18px;
}

.game-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 2px solid var(--rule);
  border-radius: 12px;
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  transition: border-color 120ms ease-out, transform 120ms ease-out;
}

.game-card:hover,
.game-card:focus-visible {
  border-color: var(--accent);
  transform: translateY(-2px);
}

/* A real ring, kept on top of the border change rather than instead of it.
   Turning the outline off and recolouring a 2px border is a focus indicator
   only somebody who already knew where focus was would find. */
.game-card:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
}

/* The card is the link, so the hover has to reach the button inside it —
   otherwise the one part that looks clickable is the one part that does not
   react. */
.game-card:hover .game-card__go,
.game-card:focus-visible .game-card__go {
  background: var(--accent-strong);
}

@media (prefers-reduced-motion: reduce) {
  .game-card { transition: none; }
  .game-card:hover, .game-card:focus-visible { transform: none; }
}

/* ---------------------------------------------------------- the picture */

.game-card__shot {
  display: block;
  width: 100%;
  /* Both needed, and the height is the load-bearing one. The width and
     height attributes on the <img> are there so the card does not jump when
     the picture arrives, but the browser maps them to real CSS width and
     height — and aspect-ratio only does anything when one of the two is
     auto. Without this line the card is 789px tall. */
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  /* Shown while the picture loads, and left showing if it never arrives.
     A card with a missing screenshot should look like a card, not like a
     broken page. */
  background: var(--surface-alt);
  border-bottom: 1px solid var(--rule);
}

/* The gallery's card has no screenshot: it is a wall of the pictures
   themselves, filled in by js/games.js from the same manifest the gallery
   and the puzzle read. Four tiles, so it never goes stale.

   The tiles carry the shape, not the box around them. The 16/9 this element
   inherits from the rule above is quietly ignored: it is a flex item in a
   column, so its height comes from its content, and a ratio only gets a say
   once the height is still auto by the time it is asked. Two rows of
   half-width 16/9 tiles come to 16/9 regardless, which is the same answer
   arrived at from the inside. */
.game-card__mosaic {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px;
}

.game-card__mosaic img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background: var(--surface-alt);
  /* A grid item's automatic minimum size is its content's intrinsic size —
     a 1600px photograph — which would win over the ratio above. */
  min-width: 0;
  min-height: 0;
}

/* The Cartoonize card, cut corner to corner: a photograph underneath and the
   same photograph cartoonized laid over the far side of the diagonal. Both
   layers are absolute and cover, so the box keeps the 16/9 it inherits above
   whatever shape the photograph happens to be.

   A diagonal rather than a vertical split, and not only because it looks
   better. A vertical one down a card this small puts the seam through the
   middle of whatever the photograph is of, which is usually a face. */
.game-card__split { position: relative; }

.game-card__split img,
.game-card__split canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Clipped only once the cartoon is actually there. Before that the canvas is
   not in the document at all, and the photograph has the card to itself. */
.game-card__split canvas {
  clip-path: polygon(38% 0, 100% 0, 100% 100%, 62% 100%);
}

/* The seam itself, so the join reads as a deliberate before-and-after rather
   than as half a picture that failed to load. Drawn on the same diagonal as
   the clip above: change one and change both. */
.game-card__split.is-drawn::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--surface);
  clip-path: polygon(38% 0, 39% 0, 63% 100%, 62% 100%);
}

/* Nothing published yet, or the manifest did not answer, or games.js never
   ran. An empty grid has no tiles to take their shape from, so the height
   comes back here — and it is a tidy block of colour rather than a hole. */
.game-card__mosaic:empty,
.game-card__split:empty {
  aspect-ratio: 16 / 9;
  background:
    linear-gradient(135deg,
      var(--accent-soft) 0%,
      var(--surface-alt) 55%,
      var(--accent-soft) 100%);
}

/* ------------------------------------------------------------- the body */

.game-card__body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  /* Fills the rest of the card so the button sits on the bottom edge of
     every card in the row, whatever the blurb does. */
  flex: 1;
  padding: 12px 14px 16px;
}

.game-card__name {
  color: var(--accent);
  font-size: calc(1.3rem * var(--font-scale, 1));
  font-weight: bold;
  line-height: 1.2;
}

.game-card__text {
  color: var(--ink-muted);
  font-size: calc(1rem * var(--font-scale, 1));
  line-height: 1.5;
  /* Pushes the meta line and the button down to the bottom. */
  flex: 1;
}

.game-card__meta {
  color: var(--ink-faint);
  font-size: calc(0.88rem * var(--font-scale, 1));
  letter-spacing: 0.02em;
}

/* A <span>, not a <button> or an <a>: the whole card is already the link,
   and nesting either inside it is invalid and steals the click. */
.game-card__go {
  display: inline-flex;
  align-items: center;
  min-height: 2.7rem;
  margin-top: 4px;
  padding: 6px 22px;
  border-radius: 7px;
  border: 1px solid var(--accent-strong);
  background: var(--accent);
  color: var(--on-accent);
  font-size: calc(1rem * var(--font-scale, 1));
  font-weight: bold;
}

@media (max-width: 560px) {
  .games-home-page .Master > section.games-home { padding: 8px 10px 20px; }
  .games-home__grid { grid-template-columns: 1fr; gap: 14px; }
}

/* The jigsaw and the gallery turn a phone away (css/bigScreenOnly.css), so on
   a phone their cards say so before the tap rather than after it. The same
   phone test as that file. */
.game-card__phone-note { display: none; }

@media (pointer: coarse) and (max-width: 720px),
       (pointer: coarse) and (max-height: 500px) {
  .game-card__phone-note {
    display: block;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--warn, #b8860b);
  }
}
