/* The game picker (the home screen). Layers, bottom to top: two still canvases (a full-resolution
   frame of the preview that was just on screen, or poster art for a game without a preview), the
   WebGL canvas with the live preview, then the picker: a dark gradient, the hero text, the join box
   and the card strip. The shell sets the layers' opacity and fades inline, so the hero always goes
   from one sharp picture to the next.
   2020 TVs run roughly Chrome 68: no `inset`, no flex `gap`, no `aspect-ratio`, and a plain
   declaration before every clamp(). */

#hero-bg { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background: var(--void); }
#hero-bg canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; }
body:not([data-phase="home"]) #hero-bg { display: none; }
/* Hidden until the first preview has drawn (the canvas may still hold the last game's frame). */
body[data-phase="home"] #gl { opacity: 0; }
body[data-phase="home"] #overlay { padding: 0; background: none; overflow: hidden; }

.picker {
  position: absolute; top: 0; right: 0; bottom: 0; left: 0;
  font-size: 18px;
  font-size: clamp(14px, 1.05vw, 22px);
}
/* Darken the left (behind the text, firmly, so any preview stays readable) and the bottom (behind
   the strip); the right stays the show. */
.picker::before {
  content: "";
  position: absolute; top: 0; right: 0; bottom: 0; left: 0;
  pointer-events: none;
  background:
    linear-gradient(90deg, rgba(4, 6, 13, 0.95) 0%, rgba(4, 6, 13, 0.88) 32%, rgba(4, 6, 13, 0.62) 46%, rgba(4, 6, 13, 0.22) 58%, rgba(4, 6, 13, 0) 70%),
    linear-gradient(0deg, rgba(4, 6, 13, 0.97) 0%, rgba(4, 6, 13, 0.75) 22%, rgba(4, 6, 13, 0) 48%),
    linear-gradient(180deg, rgba(4, 6, 13, 0.55) 0%, rgba(4, 6, 13, 0) 16%);
}

/* ---------- hero ---------- */
.hero {
  position: absolute;
  left: 5vw; bottom: 42%; /* the shell then anchors it just above the card strip */
  width: 46vw; max-width: 920px;
}
.hero.enter { animation: hero-in 0.3s ease both; }
@keyframes hero-in {
  from { opacity: 0.35; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
.hero .eyebrow { color: var(--ac, var(--muted)); text-shadow: 0 1px 6px rgba(0, 0, 0, 0.8); }
.hero-title {
  margin: 0.25em 0 0.3em;
  font: 700 112px/0.86 var(--font-display);
  font: 700 clamp(56px, 7.4vw, 168px)/0.86 var(--font-display);
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--ink);
  text-shadow: 0 4px 34px rgba(0, 0, 0, 0.65);
}
.hero-title em { display: block; font-style: normal; color: var(--amber); }
/* The description box: four lines (fewer when the column has to fit). Longer text scrolls inside it
   rather than ending in an ellipsis: the shell slides the inner line with a transform, and a mask
   softens whichever edge text is passing (only the bottom at the start, both while scrolling, only
   the top at the end). */
.hero-brief {
  margin: 0 0 1em; max-width: 46ch; max-height: 6.2em; overflow: hidden;
  font-size: 1.05em; line-height: 1.55; color: var(--ink); text-shadow: 0 1px 8px rgba(0, 0, 0, 0.9);
}
.hero-brief > span { display: block; }
.hero-brief { transition: opacity 0.3s ease; }
.hero-brief.overflows {
  -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0, #000 1em, #000 calc(100% - 1em), rgba(0, 0, 0, 0) 100%);
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-size: 100% calc(100% + 1em);
  -webkit-mask-position: 0 -1em;
  transition: opacity 0.3s ease, -webkit-mask-position 0.5s ease, -webkit-mask-size 0.5s ease;
}
.hero-brief.overflows > span { will-change: transform; }
.hero-brief.overflows.scrolling { -webkit-mask-size: 100% 100%; -webkit-mask-position: 0 0; }
.hero-brief.overflows.at-end { -webkit-mask-size: 100% calc(100% + 1em); -webkit-mask-position: 0 0; }
/* The description and the "How to play" view share one slot (the shell sizes it to the taller one);
   Down on the remote crossfades to the second view, Up back. */
.hero-swap { position: relative; }
.hero-howview { position: absolute; top: 0; left: 0; right: 0; opacity: 0; visibility: hidden; transition: opacity 0.3s ease, visibility 0s linear 0.3s; }
.hero-swap.show-how .hero-howview { opacity: 1; visibility: visible; transition: opacity 0.3s ease; }
.hero-swap.show-how .hero-brief { opacity: 0; }
.hero-kb { margin: 0 0 1em; font-size: 0.72em; }
.hero-kb:empty { display: none; }
.hero-toggle { cursor: pointer; }
.hero-how { display: grid; grid-template-columns: auto minmax(0, 1fr); grid-gap: 0.35em 0.9em; gap: 0.35em 0.9em; margin: 0 0 0.8em; font-size: 0.82em; }
.hero-how dt { font: 700 0.95em/1.3 var(--font-display); letter-spacing: 0.08em; text-transform: uppercase; color: var(--signal); }
.hero-how dd { margin: 0; line-height: 1.4; color: var(--muted); }
.hero .prompt { text-shadow: 0 1px 8px rgba(0, 0, 0, 0.9); }
.hero-note { margin: 0.5em 0 0; font-size: 0.72em; }
.hero-note:empty { display: none; }
/* The column must clear the top of the safe area: the shell steps it down until it does. fit-1: a
   smaller title; fit-2: a three-line description box (it scrolls, so nothing is lost); fit-3: a
   compact "How to play" list; fit-4: an even smaller title; fit-5: a two-line box. */
.hero.fit-1 .hero-title, .hero.fit-2 .hero-title, .hero.fit-3 .hero-title { font-size: 96px; font-size: clamp(50px, 6.3vw, 143px); }
.hero.fit-4 .hero-title, .hero.fit-5 .hero-title { font-size: 80px; font-size: clamp(44px, 5.2vw, 118px); }
.hero.fit-2 .hero-brief, .hero.fit-3 .hero-brief, .hero.fit-4 .hero-brief { max-height: 4.65em; }
.hero.fit-5 .hero-brief { max-height: 3.1em; }
.hero.fit-3 .hero-how, .hero.fit-4 .hero-how, .hero.fit-5 .hero-how { font-size: 0.74em; grid-gap: 0.15em 0.9em; gap: 0.15em 0.9em; }
/* Reduced motion: nothing scrolls. The whole description shows, a little smaller and across the
   column's full width, and the fit levels above make room for it. */
@media (prefers-reduced-motion: reduce) {
  .hero-brief, .hero.fit-2 .hero-brief, .hero.fit-3 .hero-brief, .hero.fit-4 .hero-brief, .hero.fit-5 .hero-brief { max-height: none; max-width: none; font-size: 0.9em; line-height: 1.45; }
  .hero.fit-5 .hero-brief { font-size: 0.8em; }
  .hero.enter { animation: none; }
}
.hero .note { margin-top: 0.6em; }

/* ---------- join box ---------- */
.join {
  position: absolute; top: 5vh; right: 5vw; /* inside the TV safe area */
  display: flex; align-items: flex-start;
  max-width: 30vw;
  padding: 0.75em;
  background: rgba(4, 9, 15, 0.78);
  border: 1px solid var(--rule);
}
#qr { flex: none; width: 112px; width: clamp(88px, 6.6vw, 150px); height: auto; image-rendering: pixelated; background: var(--qr-light); }
.join-info { min-width: 0; margin-left: 0.85em; }
.join .eyebrow { font-size: 0.66em; }
.join .code { margin: 0.1em 0 0.15em; font: 700 2.1em/1 var(--font-display); letter-spacing: 0.16em; color: var(--amber); }
.join-url { margin: 0 0 0.5em; font-size: 0.72em; line-height: 1.35; color: var(--muted); word-break: break-all; }
.roster { display: flex; flex-wrap: wrap; list-style: none; margin: 0 0 -0.3em; padding: 0; }
.roster li { display: flex; align-items: center; margin: 0 0.55em 0.3em 0; font-size: 0.72em; white-space: nowrap; }
.roster li i { width: 0.7em; height: 0.7em; margin-right: 0.3em; background: var(--c); }
.roster li b { margin-right: 0.3em; font: 700 1em/1 var(--font-display); color: var(--c); }
.roster li span { font: 700 1em/1 var(--font-display); letter-spacing: 0.04em; text-transform: uppercase; }
.roster li.away { opacity: 0.45; }
.join-count { margin: 0.45em 0 0; font-size: 0.66em; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }

/* ---------- card strip ---------- */
/* Cards sit 8vh up, clear of the hints line. The generous top and bottom padding (offset in `bottom`)
   keeps the focused card's lift, focus ring and shadow inside the clip; 5vw at the sides is the safe
   area the shell scrolls the cards within. */
.shelf {
  position: absolute; left: 0; right: 0; bottom: calc(8vh - 50px);
  overflow: hidden;
  padding: 60px 5vw 50px;
}
.shelf-row { display: flex; transition: transform 0.35s ease; }
.gcard {
  flex: none;
  position: relative;
  width: 220px;
  width: clamp(170px, 14.5vw, 300px);
  margin-right: 1.1vw;
  padding: 0;
  text-align: left;
  font: inherit;
  color: var(--ink);
  background: #0a1320;
  border: 2px solid rgba(95, 243, 255, 0.16);
  border-bottom: 4px solid var(--ac, var(--signal));
  cursor: pointer;
  transform-origin: 50% 100%;
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
/* 16:9 thumbnail without aspect-ratio: the padding box sets the height. */
.gcard .thumb { position: relative; display: block; padding-top: 56.25%; overflow: hidden; }
.gcard canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: block; }
.gcard .ct { display: block; padding: 0.55em 0.7em 0.15em; font: 700 0.92em/1.15 var(--font-display); letter-spacing: 0.06em; text-transform: uppercase; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gcard .cp { display: block; padding: 0 0.85em 0.6em; font-size: 0.68em; color: var(--muted); }
.gcard .votes { position: absolute; top: 6px; right: 6px; display: flex; }
.gcard .votes i { width: 12px; height: 12px; margin-left: 4px; background: var(--c); border: 1px solid rgba(0, 0, 0, 0.6); }
.gcard:hover { border-color: rgba(95, 243, 255, 0.4); }
/* Plain :focus (remote users need it; old TVs drop :focus-visible). */
.gcard:focus {
  outline: none;
  z-index: 2;
  transform: translateY(-8px) scale(1.09);
  border-color: var(--amber);
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.7), 0 0 0 2px var(--amber);
}
.gcard:focus .ct { color: var(--amber); }
.gcard.off { opacity: 0.5; }
.gcard.off:focus { opacity: 0.85; }
.picker-meta { position: absolute; left: 5vw; right: 5vw; bottom: 5vh; margin: 0; font-size: 0.68em; line-height: 1.6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.picker-meta span + span::before { content: " · "; }
.picker .shelf .brief { margin: 0; }
