/* Scripts catalogue. Same rule as the shell: no colour, no duration, no radius
 * that is not a token. */

.phead {
  padding: var(--s-6) var(--s-6) var(--s-5);
  border-bottom: 1px solid var(--ln);
  position: relative; overflow: hidden;
  background: radial-gradient(560px circle at var(--mx, 50%) var(--my, 0%), var(--spot), transparent 62%);
}
.phead > * { position: relative; z-index: 2; }
.phead h1 { margin: 0 0 7px; font-size: var(--t-h2); }
.phead .sub { margin: 0; color: var(--mut); font-size: var(--t-sm); max-width: 62ch; }

/* The filter row aligns to the input, not to the field. The field is taller
   because it carries a label above it, so centring put the chips against the
   label rather than the box they filter. */
/* .filters, .chips and .count moved to shell.css. They were used by this page
   and by the executors page, and living here meant the executors page shipped
   four unstyled buttons: grey boxes with black text. */
#list { padding: var(--s-2) 0 var(--s-6); }

/* ── a catalogue row ────────────────────────────────────────────────────── */
.game { border-bottom: 1px solid var(--ln); }
.ghead {
  display: grid; grid-template-columns: auto 1fr auto auto; gap: var(--s-4); align-items: center;
  width: 100%; min-width: 0; padding: var(--s-3) var(--s-6);
  background: none; border: 0; font: inherit; color: inherit; text-align: left; cursor: pointer;
  position: relative;
  transition: background-color var(--t-tint) ease, padding-left var(--t-move) var(--ease);
}
.ghead::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--acc); box-shadow: 0 0 12px -1px var(--acc);
  transform: scaleY(0); transform-origin: top;
  transition: transform var(--t-move) var(--ease);
}
.ghead:hover { background: var(--hov); padding-left: var(--s-8); }
.ghead:hover::before { transform: scaleY(1); }
.ghead[disabled] { cursor: default; opacity: 0.6; }
.ghead[disabled]:hover { background: none; padding-left: var(--s-6); }
.ghead[disabled]:hover::before { transform: scaleY(0); }
.ghead .thumb { width: 46px; height: 46px; }
.gname { min-width: 0; }
/* Truncation is the last resort, not the first. At full width these fit; when
   they do not, two lines beats an ellipsis on the one thing being scanned for. */
.gname em {
  font-style: normal; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; font-size: var(--t-body); font-weight: 500; line-height: 1.3;
}
.gmeta { font-size: var(--t-xs); color: var(--mut); margin: 2px 0 0; }
.state { font-size: 11.5px; color: var(--mut); }
.state[data-s="working"] { color: var(--ok); }
.state[data-s="maintenance"] { color: var(--warn); }

.viewf { display: inline-flex; align-items: center; gap: 7px; font-size: var(--t-xs); color: var(--mut); white-space: nowrap; transition: color var(--t-tint) ease; }
.ghead:hover .viewf { color: var(--acc-txt); }
.viewf .ic { width: 14px; height: 14px; }
.viewf .ic-arw { opacity: 0; transform: translateX(-7px); transition: opacity var(--t-tint) ease, transform var(--t-move) var(--ease); }
.ghead:hover .viewf .ic-arw { opacity: 1; transform: none; }
/* The phone row, which was losing the three things it is read for.
 *
 * Below 620px the status word was hidden and the action collapsed to
 * `font-size: 0`, and below 560px a shell rule hid the thumbnail as well. So on
 * a phone a catalogue row showed a name, a count, and a stray icon floating at
 * whatever horizontal position the name happened to end at. The art and the
 * status are the two fastest ways to recognise a game and to know whether it
 * works, and this page exists to answer exactly those.
 *
 * They come back, smaller. What goes instead is the "Open" affordance, which was
 * never load-bearing: the whole row is a link now, and an arrow explaining that
 * a link is a link costs width a phone does not have. */
@media (max-width: 620px) {
  .ghead {
    grid-template-columns: auto 1fr auto;
    gap: var(--s-3);
    padding-inline: var(--s-4);
  }
  .ghead .thumb { display: block; width: 38px; height: 38px; }
  .ghead .state { font-size: 10.5px; white-space: nowrap; }
  .viewf { display: none; }
  .ghead:hover { padding-left: var(--s-4); }
}

.empty { padding: var(--s-10) var(--s-6); text-align: center; color: var(--mut); font-size: var(--t-sm); }
.empty b { display: block; color: var(--fg); font-size: var(--t-h3); margin-bottom: 6px; font-weight: 600; }

/* The feature panel's styles went with the panel.
 *
 * A dialog, a search field, a group rail, feature rows and a footer: about a
 * hundred and ten lines describing markup that nothing renders any more, sitting
 * in a stylesheet every visitor to this page downloads. Dead CSS is quieter than
 * dead code and lasts longer for it, because nothing errors and nothing is
 * missing; the page simply carries it forever.
 *
 * A script opens as its own page now. See frontend/v2/game.css.
 */


/* ── the controls a feature actually gets ───────────────────────────────── */
/* The in-menu control mockups (.ctl, .sw, .sld, .dd) moved to shell.css so the
   home page can show the same widgets. They were used by two pages and lived in
   one page stylesheet, which is the third time that has bitten this week. */


/* ONE game per line, beside the redeem panel.
 *
 * It was two, on the reasoning that 62 rows across the full width is a very long
 * page with a very empty right-hand side. That was true before the redeem panel
 * moved in beside it, and false afterwards: the panel takes 22rem of an 1180rem
 * measure, so each of the two columns ended up around 415px, and a row is a
 * 46px thumbnail, a status word, an action and whatever is left for the name.
 *
 * Whatever is left was about 150px, and these names are long: an emoji prefix, a
 * bracketed tag and a title. Production showed "[ Build a Ri...", "[UPD] Carrot
 * Tow..." and "[NEW HORIZONS]...", so the one thing somebody scans this list for
 * was the one thing cut off. The meta line under it wrapped to two lines in the
 * left column and one in the right, which made the two columns disagree about
 * row height as well.
 *
 * A single column gives a row the full 830px. The name fits, the meta fits on
 * one line, and every row is the same height. The list already scrolls inside
 * itself, so the extra length costs nothing.
 */
#list {
  display: grid;
  grid-template-columns: 1fr;
  align-content: start;
  gap: 0;
}
#list > .count { grid-column: 1 / -1; }
.game { border-bottom: 1px solid var(--ln); min-width: 0; }

/* The sidebar sticks while the catalogue scrolls, because the whole point is not
   having to leave the list to redeem. */
@media (min-width: 881px) {
  #sidebar { position: sticky; top: var(--s-4); align-self: start; }

  /* And the catalogue scrolls INSIDE ITSELF rather than dragging the page.
   *
   * Asked for by an admin looking at the live page: 62 games at two per line is
   * a very tall column, and scrolling it moved the header, the filters and the
   * redeem panel off screen together. Sticky positioning kept the panel in view
   * but the search field and the status chips still left, so filtering meant
   * scrolling back up to the control you had just used.
   *
   * Only above 881px. Below that the panel is stacked under the list and there
   * is no second column to hold still, so a nested scroller would be a smaller
   * viewport inside a viewport, which is worse on a phone than plain page scroll.
   *
   * The height is viewport-relative with a floor: on a short laptop screen a
   * percentage alone can collapse to a two-row window. */
  #list {
    max-height: clamp(26rem, calc(100vh - 19rem), 62rem);
    overflow-y: auto;
    overscroll-behavior: contain;   /* stop at the ends, don't chain to the page */
    scrollbar-gutter: stable;
    padding-right: var(--s-2);
  }
  /* Reachable and scrollable from the keyboard. A div with overflow is scrollable
     by mouse and, without this, invisible to Tab, so a keyboard user can reach
     every game link and never the container that has to move to show them. */
  #list:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; }

  /* A soft top and bottom edge, so a row cut in half reads as "there is more"
     rather than as the end of the catalogue. `mask` rather than an overlay,
     because an overlay sits above the rows and eats the clicks on them.
     A mask reads only the ALPHA of these stops, so which opaque token is named
     here makes no visual difference; --fg is used because it is opaque and it is
     a token, and the sheet is gated against raw colours for good reasons that
     apply to real colours. 24px is the fade distance, a geometric fact. */
  #list {
    mask-image: linear-gradient(to bottom, transparent 0, var(--fg) 24px,
                                var(--fg) calc(100% - 24px), transparent 100%);
  }
}

/* Someone who has asked for less motion still gets the scroller; smooth
   scroll-behaviour is the part that goes. */
@media (prefers-reduced-motion: no-preference) {
  #list { scroll-behavior: smooth; }
}
