/* The catalog region: the "All games" heading, the DOM card grid and its empty state (DESIGN §15.3,
   §15.10; CONTRACTS §5.6). The cards are kit.css .k-card twins (their CSS theme: no WebGPU, the boot
   frame, device loss); in gpu-paint mode catalog/cards.js paints them from their untransformed rects.
   Owner: catalog (design/CONTRACTS.md). Loaded by site/index.html; portal/build.sh versions it. */

/* Grid: 3 columns at lg/xl (>= 1200), 2 at md, 1 below 760; gap 28, row gap 40 / 28 (room for drips and
   a perched birb), gutter 36 / 16 (or the safe area + 8), content max 1528. */
#catalog {
  --catalog-gutter: var(--gutter, 36px);
  position: relative; box-sizing: border-box; width: 100%;
  max-width: calc(var(--content-max, 1528px) + 2 * var(--catalog-gutter));
  margin: 0 auto;
  padding: 0 max(var(--catalog-gutter), calc(env(safe-area-inset-right, 0px) + 8px)) 96px
    max(var(--catalog-gutter), calc(env(safe-area-inset-left, 0px) + 8px));
}
.catalog-heading {
  margin: 44px 0 30px; color: var(--text-hi, #FFF4E0);
  /* the skip link's target lands below the stuck pad (pad 76 + top 10 + gutter + scrim) */
  scroll-margin-top: 128px;
}
#portal-catalog {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: stretch;
  column-gap: var(--card-gap, 28px); row-gap: var(--row-gap, 40px);
}
/* #portal-page is click-through (CONTRACTS §5.5): the card twins and the empty state take the pointer. */
#portal-catalog > .k-card, #catalog-empty { pointer-events: auto; }
/* keyboard focus scrolls a card clear of the stuck pad */
#portal-catalog > .k-card { scroll-margin-top: 128px; scroll-margin-bottom: 24px; }
/* ...but while the pad is stuck, toolbar.css's scroll-padding-top (125: the pad's band) already clears it: the two must
   not add up (a focused card landed 253 px down, and a Tab out of the pad scrolled it up 52 px and unstuck the pad).
   The heading then lands on the band's line, like a search's results (toolbar STUCK_LINE); a card 20 px under it, room
   for its lift and halo (cards.js CATALOG_CLIP_MARGIN). */
html:has(#catalog-toolbar.is-stuck:not(:focus-within)) .catalog-heading { scroll-margin-top: 0; }
html:has(#catalog-toolbar.is-stuck:not(:focus-within)) #portal-catalog > .k-card { scroll-margin-top: 20px; }
@media (max-width: 1199.98px) {
  #portal-catalog { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 759.98px) {
  /* one column, at most 600 wide (centred on portrait tablets like the hero); the toolbar's footer#portal-footer
     (Privacy) follows and carries the dock clearance itself */
  #catalog { max-width: calc(600px + 2 * var(--catalog-gutter)); padding-bottom: 24px; }
  #portal-catalog { grid-template-columns: minmax(0, 1fr); }
  .catalog-heading { margin: 36px 0 24px; scroll-margin-top: 24px; }
  /* keyboard focus keeps a card clear of the dock (64 + lip 6 + its bottom max(12, safe area + 6) + 16) */
  #portal-catalog > .k-card { scroll-margin-top: 24px; scroll-margin-bottom: calc(98px + env(safe-area-inset-bottom, 0px)); }
}

/* Card titles (DESIGN §15.3, §5): never split a word, at most 2 lines. catalog/grid.js (fitTitles) gives each
   shown title the first size that fits: 30 (25) px, else 26 (21) (kit.css .k-card__title--long), else, so that
   a long name is not cut, 22 / 20 / 18 / 16 (phones 19 / 17 / 16 / 15) below. Only at the smallest size may
   an over-wide word break, and a third line is clamped with an ellipsis (the GPU paints it on its sprite; the
   twin keeps the whole title). */
#portal-catalog .k-card__title { overflow-wrap: normal; }
#portal-catalog .k-card__title.catalog-title--s { font-size: 22px; --k-lip: 2px; }
#portal-catalog .k-card__title.catalog-title--xs { font-size: 20px; --k-lip: 2px; }
#portal-catalog .k-card__title.catalog-title--xxs { font-size: 18px; --k-lip: 2px; }
#portal-catalog .k-card__title.catalog-title--min { font-size: 16px; --k-lip: 2px; }
#portal-catalog .k-card__title.catalog-title--break { overflow-wrap: anywhere; }
#portal-catalog .k-card__title.catalog-title--clamp { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
@media (max-width: 759.98px) {
  #portal-catalog .k-card__title.catalog-title--s { font-size: 19px; }
  #portal-catalog .k-card__title.catalog-title--xs { font-size: 17px; }
  #portal-catalog .k-card__title.catalog-title--xxs { font-size: 16px; }
  #portal-catalog .k-card__title.catalog-title--min { font-size: 15px; }
}

/* gpu-paint: the GPU draws the posters, box art and critters of a painted card (data-gpu): its CSS-theme
   images are not even decoded (DESIGN §16.1: no picture decodes twice). They are absolutely placed or sit
   in a fixed-size frame, so this changes no layout. */
html.gpu-paint #portal-catalog > .k-card[data-gpu] img { display: none; }

/* Each game's goo tint on the CSS theme's goo (GOO-VARIANTS §7): grid.js sets --k-goo-rest / --k-goo-awake (the kit's
   gooFilter(), equal to the shader's tint) on every styled card, so the boot frame's rims and box-art globs already
   wear the colour the GPU paints them in, and the hand-off (or a device loss) keeps it: the rim at rest, awake on the
   same states as kit.css's awake rim (a SOON card stays at rest), the glob in the awake ramp like the GPU's. Not in
   the CSS theme without WebGPU (house goo, GOO-VARIANTS §12 q3); fillers have no variables (house). */
:root:not(.gpu-unavailable) #portal-catalog > .k-card .k-card__window::after { filter: var(--k-goo-rest, none); }
:root:not(.gpu-unavailable) #portal-catalog > .k-card:not(.k-card--soon):is(:hover, :focus-visible, .is-awake) .k-card__window::after,
:root:not(.gpu-unavailable) #portal-catalog > .k-card > .k-boxart::before { filter: var(--k-goo-awake, none); }

/* Empty search / filter (DESIGN §15.10): Puddle (surprised "o"), "No games found." / "Try another word."
   and a cream Clear search key. Text on the bare void is visible DOM (§16.1 R1); the key is a CSS body. */
#catalog-empty {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 28px 16px 64px; text-align: center;
}
#catalog-empty[hidden] { display: none; }
.catalog-empty__art { width: 156px; height: auto; margin-bottom: 6px; cursor: pointer; transform-origin: 50% 88%; -webkit-tap-highlight-color: transparent; }
/* the only Puddle gag: a click squishes it flat, then it slides back up */
.catalog-empty__art.is-squished { animation: catalog-squish 900ms var(--ease-goo-pop, cubic-bezier(.34, 1.56, .64, 1)) both; }
@keyframes catalog-squish {
  0% { transform: none; }
  14% { transform: scale(1.28, .34); }
  58% { transform: scale(1.2, .42); }
  80% { transform: scale(.94, 1.07); }
  100% { transform: none; }
}
.catalog-empty__title { margin: 6px 0 0; color: var(--text-hi, #FFF4E0); }
.catalog-empty__text { margin: 0; color: var(--text, #E8DDF7); font: 400 var(--fs-body-m, 16px) / var(--lh-body-m, 24px) var(--f-body, "Fredoka", system-ui, sans-serif); }
.catalog-empty__clear { margin-top: 18px; }
