/* The shelf on `/gifting`: `game-grid` narrowed for the one page that SELLS off
 * its tiles instead of reporting with them.
 *
 * Its own sheet and not four lines in `pages.css`, which stood at 749 of a hard
 * 750 the day this was written - the same reason `home.css` split out of
 * `app.css` on 2026-08-25. Linked immediately after `pages.css` so nothing else
 * changes place in the cascade, and it has to be after it: `.gift-shelf` and
 * `.game-grid` have equal specificity, so link order is the only thing deciding
 * which track wins.
 *
 * Every rule here is scoped to `gift-shelf` or to a class no other page draws.
 * `game-grid`, `game`, `game-ic` and `game-state` are shared with the rent
 * listing and the `/analytics` roster, and the `<link>` is unconditional, so a
 * bare rule here would reach both of them. */

/* `auto-fill` keeps the empty tracks it makes, so the four games we sell sat in
 * the first four of ten 104px columns on a 1320px panel with two thirds of the
 * row blank beside them: a shelf reads as a table when nothing on it is bigger
 * than a row. `auto-fit` collapses the empty tracks and the tiles take the
 * width. The 150px floor is `gm-grid`'s, measured there - 150 still fits twice
 * on a 26rem phone, so the shelf is two across above the fold. That is MORE than
 * one column on a phone, which is why `gift-shelf` sits beside `game-grid` in
 * `responsive.rs::EXEMPT` rather than taking a 1fr breakpoint. */
.gift-shelf {
  /* 136 and not the 150 this floor was, because the gutter below grew and the
   * two are spent out of the same 320px a 360px phone gives: at 150 with a 24px
   * gutter two tracks need 324 and the shelf dropped to one column there. The
   * floor is what an auto-repeat counts with here - the maximum is `1fr` and so
   * indefinite - and it changes nothing above the phone, since `auto-fit`
   * collapses the tracks the four games do not fill and the four that remain
   * stretch across the wrap whatever the floor says. */
  grid-template-columns: repeat(auto-fit, minmax(136px, 1fr));
  /* `game-grid`'s 12px was a gap between two bordered cards, where the border
   * did the telling apart. With the card gone it is the only thing that does,
   * and 12px of it ran four full-bleed pictures together into one strip. The
   * row gap is larger than the column gap on purpose: a caption sits under
   * every tile, so down the page the next picture has to clear three lines of
   * type rather than nothing. */
  gap: var(--step-8) var(--step-6);
}

/* THE ART IS THE TILE, since 2026-08-26. The rent listing asks "can this account
 * send in that game" and 72px is the right size for an answer standing beside a
 * name. This page is a shop, and 120px centred inside a 324px card left the
 * picture holding about an eighth of the tile with the rest chrome and air. The
 * operator's brief is that a picture should speak for itself; that one was a
 * stamp on a form. The card goes, and the art takes the whole track.
 *
 * Nothing is drawn in the card's place. Two tiles are told apart by the gap
 * below, which is what `deleting-copy.md` means by separation being whitespace
 * and not a rule, and the files are 512x512 so there is sharpness to spend. */
.gift-shelf .game-ic {
  max-width: none;
  border-radius: var(--r-lg);
  object-fit: cover;
  margin-bottom: var(--step-2);
  transition: box-shadow 120ms ease;
}

/* The affordance the deleted border was carrying: `.gm-link:hover` moves a
 * `border-color`, and there is no border here any more. The ring goes on the
 * art rather than the whole tile, because the art is what the press opens.
 * Focus needs no rule - the global `:focus-visible` outline is on the anchor,
 * which is now the art and its caption. */
.gift-shelf .gm-link:hover .game-ic {
  box-shadow: 0 0 0 1px var(--accent);
}

/* A game with no file on disk. The tinted square the shared rule leaves is
 * right at 72px on a rent listing and is a hole at tile size, so it carries the
 * site's own mark instead: `/static/favicon.svg`, reused rather than a second
 * drawing invented, per `storefront-design`. The mark itself HAS been redrawn
 * twice and this follows it, which is the point of pointing at the file.
 * A pseudo-element and not a second background layer,
 * because the opacity is the whole difference between a placeholder and a logo.
 * A CSS `url()` is `img-src`, and the file is same-origin, so the CSP allows it
 * the way it already allows the grain. */
.gift-shelf span.game-ic {
  position: relative;
}
.gift-shelf span.game-ic::after {
  content: "";
  position: absolute;
  inset: 0;
  background: url("/static/favicon.svg") center / 26% no-repeat;
  opacity: .45;
}

/* The price is what a signed out visitor came to read: the picker is behind the
 * sign in wall, so this shelf is the whole shop for them. It was a third line in
 * `.game-state`, the 10px letter-spaced mono this site uses for LABELS, which
 * set the name, the stock and the price at one weight each and repeated all
 * three down every tile. That is the look the operator read as a compliance
 * table. One figure on the tile is set to be READ; the rest is scanned. */
.game-price {
  font-size: 15px;
  color: var(--text-1);
}

/* The tile stretches to its row, so its own rows share the slack: one game
 * carrying a condition badge pushed the name, the price and the count of every
 * tile beside it out of line, and a shelf is read ACROSS, not down.
 * `align-content: start` puts the leftover pixels at the bottom instead of
 * between the lines.
 *
 * The rest of this rule is the card being taken off. Left aligned and not
 * centred, so the three caption lines hang off the same edge the picture above
 * them starts at - a shelf of products rather than four cards of centred text.
 * 4px between those lines, so they read as one caption block under the art
 * instead of three rows at equal remove from each other. */
.gift-shelf .game {
  align-content: start;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  gap: var(--step-1);
  justify-items: stretch;
  text-align: left;
}

/* The tile became an `<a class="game gm-link">` INSIDE the `li` on 2026-08-26,
 * so the shelf now has the shape `/analytics` already had and needs the fix
 * that shape needs: the `li` is the grid item and stretches, the anchor inside
 * it does not, so without this a row of tiles ends at several heights and the
 * price on each sits at a different one. This paragraph used to say the two
 * could not be shared BECAUSE the classes sat on different elements; they sit
 * on the same one now, and `.gm-link` itself is reused rather than copied. */
.gift-shelf > li {
  display: grid;
}

/* One game's own list: `/gifting/game/{slug}`. Same shelf, one level down, so
 * it is the same tile with a longer name in it.
 *
 * BOUNDED at 150 and not `1fr` since 2026-08-26. A bound is needed because
 * `auto-fit` collapses its empty tracks and hands every leftover pixel to the
 * ones that are left: a game selling three items drew three 430px cards each
 * holding a 64px picture in the middle, and a game selling ONE draws a single
 * tile the full 1320px of the wrap. Upstream sets that count and the template
 * already writes the singular, so one item is a shape this page must render.
 * The live shelves hold 54, 17, 11 and 7 items. Seven 150px columns fill the
 * row at 1440: `.wrap` is `min(1320px, 100% - 40px)` inside `body`'s 60px rail
 * padding, so 1280px, and seven tracks with six 16px gutters need 1146 where
 * eight would need 1312. This said EIGHT, and read the art counts as the item
 * counts, which is how five missing pictures were described as a column break.
 *
 * THE BOUND DECIDES THE COLUMN COUNT, NOT THE FLOOR. This comment said the
 * opposite for one build, and the sheet under it was wrong by the same
 * reasoning: an auto-repeat is counted with the track's MAXIMUM whenever that
 * maximum is a definite length, and only falls back to the minimum when it is
 * `1fr` or another indefinite one. `minmax(140px, 168px)` therefore needed
 * 2x168 rather than 2x140 and dropped a 390px phone to ONE column, which
 * measuring the resolved `grid-template-columns` showed and reading the rule
 * did not. At 150 with a 16px gutter two tracks need 316 against the 320 a
 * 360px phone gives, which is the narrowest viewport walked. */
.item-grid {
  grid-template-columns: repeat(auto-fit, minmax(140px, 150px));
  gap: var(--step-6) var(--step-4);
}

/* The same card taken off the same tile, for the shelf one level up's reason.
 * These two are meant to be one thing - this sheet's own note above says "the
 * same tile with a longer name in it and a wider track" - so stripping the
 * chrome from one and leaving it on the other would make them two.
 *
 * The picture does NOT grow to match. `item_icons.py` fetches 150px files, so
 * 75 is the ceiling on a devicePixelRatio 2 screen and 64 is already close to
 * it. What changes is the share: 64px inside a 168px track is 43% of the tile
 * where the same picture inside a 240px card was 27%, and the card was the rest
 * of it. Left aligned like the games above, so the caption hangs off the edge
 * the picture starts at. */
.item-grid .game {
  align-content: start;
  justify-items: stretch;
  text-align: left;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  gap: var(--step-1);
}

/* The item's own picture, at 64px. The file is 150px square, which covers a
 * devicePixelRatio 2 screen outright and is the whole bandwidth question: we
 * serve these bytes ourselves, because the CSP is `img-src 'self' data:` and
 * hotlinking Roblox would both be dropped by the browser and hand Roblox the
 * address of every visitor. `scripts/item_icons.py` fetches them once, by hand.
 *
 * `aspect-ratio` beside the width, and `width`/`height` on the tag itself, so
 * the square is reserved before the bytes land. Fifty four pictures arriving one
 * at a time into an unreserved layout is the whole of a bad CLS score. */
.item-ic {
  width: 64px;
  aspect-ratio: 1;
  height: auto;
  border-radius: var(--r-md);
  margin-bottom: var(--step-3);
}

/* There is no rule for an item with no art, because there is no such card.
 * `gift_games::item_icon` hands every item a `src` - its own picture when one
 * is on disk, and `/static/img/item-fallback.webp`, Roblox's own mark, when
 * there is not. `.item-plain` and its 64px `::before` spacer came out with the
 * branch on 2026-08-30: they existed to hold the picture's box open over a gap,
 * and nothing opens a gap now. */

/* The name is what somebody came to read, so it is set to be read: 15px against
 * the 13 the game tiles carry, where a tile's name is a caption under art that
 * has already identified the game. An item's picture identifies far less on its
 * own - one fruit looks much like another - so the name stays the loudest thing
 * on the card. */
.item-name {
  font-size: 15px;
  color: var(--text-1);
  line-height: 1.3;
}

/* Upstream's sentence about a randomised item. Quiet and small: it is a
 * condition of listing the item, so it has to be there, and it is not what the
 * card is for. Four of the eighty nine items carry one. */
.item-note {
  font-size: 12px;
  color: var(--text-3);
  line-height: 1.4;
}

/* Under the title, in the left column, and it is the fourth paragraph on this
 * site to need saying so. `pages.css` puts every direct paragraph of a page head
 * into column two row two, which is where the art below sits, and the two
 * superimposed: "54 items, 25 to 5,000 R$" read straight through a picture of
 * Blox Fruits. Same escape `.doc-date` and `.balance-line` take there, and it is
 * the right cell on its own terms - the count is what somebody opened this page
 * for, so it belongs under the name of the game rather than off to the side. */
.page-head .wrap > p.shelf-count {
  grid-column: 1;
  grid-row: 3;
  margin-top: var(--step-4);
  max-width: none;
  padding-bottom: 0;
}

/* The product art, in the cell every page head keeps for a form card. This page
 * has no form, and a shop leads with the thing it sells. Spanning the head's
 * three rows rather than sitting in one of them, because `.page-head .wrap` is
 * `align-items: center` and a 240px square in row one would hang off the top of
 * a 66px heading. */
.head-art {
  grid-column: 2;
  grid-row: 1 / span 3;
  justify-self: center;
  width: 100%;
  max-width: 240px;
  height: auto;
  border-radius: var(--r-lg);
}

/* The head collapses to one column at 1100 (`motion.css`), and a `grid-column:
 * 2` surviving that would open a second implicit column and put the art beside
 * a text column half the width. Same escape `.page-head h1` takes there, in
 * this sheet because the rule it is escaping from is in this one. */
@media (max-width: 1100px) {
  .head-art {
    grid-column: 1;
    grid-row: auto;
    justify-self: start;
    max-width: 128px;
  }
}
