/* Hub page. Ported rule for rule from ShareAnApp's app.css (base layer,
   top bar, recent row, card grid). Semantic tokens from tokens.css only.
   The account surfaces live in studio-auth.css, which games load too. */

* {
  box-sizing: border-box;
}

*,
*::before,
*::after {
  scrollbar-width: none;
}

*::-webkit-scrollbar {
  display: none;
}

html,
body {
  margin: 0;
  padding: 0;
  min-height: 100%;
  overscroll-behavior: none;
}

body {
  background: var(--color-bg-page);
  color: var(--color-ink);
  font: var(--text-role-body);
}

body,
button,
input {
  -webkit-font-smoothing: antialiased;
}

button {
  font: var(--text-role-body);
  color: var(--color-ink);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

input {
  font-family: var(--font-ui);
}

a {
  color: var(--color-link);
  text-decoration: none;
}

a:hover {
  color: var(--color-link-hover);
}

h1,
h2,
h3,
h4,
p,
ul {
  margin: 0;
  padding: 0;
}

ul {
  list-style: none;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.home {
  min-height: 100vh;
}

/* Shared icon button, the sort pair uses it. */
.icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--height-button);
  height: var(--height-button);
  border-radius: var(--radius-button);
  color: var(--color-ink-secondary);
  transition: var(--transition-hover);
}

.icon-button:hover {
  background: var(--color-hover);
}

.icon-button:active {
  background: var(--color-active);
}

.icon-button:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.icon-button-current,
.icon-button-current:hover {
  background: var(--color-accent-tint);
  color: var(--color-accent-ink-strong);
}

/* ------------------------------------------------------------------ */
/* Top bar                                                              */
/* ------------------------------------------------------------------ */

.topbar {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--space-4);
  height: var(--topbar-height);
  padding: 0 calc(var(--space-3) - var(--space-2));
  background: var(--color-bg-page);
}

.topbar-right .avatar {
  width: var(--topbar-mark-size);
  height: var(--topbar-mark-size);
  font-size: calc(var(--topbar-mark-size) * 0.44);
}

.topbar-right .avatar-button {
  padding: var(--space-2);
}

.topbar-left {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex: 1 1 0;
  min-width: var(--topbar-side-min);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
}

.brand:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.brand svg {
  width: var(--topbar-mark-size);
  height: var(--topbar-mark-size);
}

/* The wordmark SVG in the brand lockup (docs/BRAND.md): its box is 0.55
   of the mark tile's height, 22 px beside the 40 px tile, a --space-2 gap
   apart. Drawn in --color-ink, the same as every other lockup. */
.brand .brand-wordmark {
  display: block;
  width: auto;
  height: var(--text-2xl);
  aspect-ratio: 180 / 40;
  color: var(--color-ink);
}

.topbar-middle {
  flex: 0 1 var(--search-max-width);
  min-width: 0;
  display: flex;
  justify-content: center;
}

.topbar-search {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  max-width: var(--search-max-width);
  height: var(--height-searchbar);
  padding: 0 var(--space-4);
  background: var(--color-search-fill);
  border-radius: var(--radius-pill);
  color: var(--color-ink-secondary);
  transition: var(--transition-hover);
}

.topbar-search:focus-within {
  background: var(--color-surface);
  box-shadow: var(--searchbar-elevation-focus);
}

.topbar-search input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font-size: var(--text-lg);
  color: var(--color-ink);
}

.topbar-search input::placeholder {
  color: var(--input-placeholder);
}

/* The native clear cross would be a second, unstyled glyph in the pill. */
.topbar-search input::-webkit-search-cancel-button {
  -webkit-appearance: none;
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: 1 1 0;
  min-width: var(--topbar-side-min);
  margin-left: auto;
  justify-content: flex-end;
}

/* Signed out, the language pill and the sun or moon sit beside the
   account button. Signed in, both live in the account panel instead, so the
   bar hides them; until the session is known they stay hidden, so a
   signed-in player never sees them flash in and out. */
.topbar-right .topbar-toggle {
  display: inline-flex;
  align-items: center;
}

html:not(.studio-signed-out) .topbar-right .topbar-toggle {
  display: none;
}

@media (max-width: 620px) {
  .topbar {
    --topbar-side-min: calc(var(--topbar-mark-size) + 2 * var(--space-2));
  }

  .brand .brand-wordmark {
    display: none;
  }

  /* The theme and language choices live in the account panel on phones. */
  .topbar-right .topbar-toggle {
    display: none;
  }
}

/* ------------------------------------------------------------------ */
/* Games row and grid                                                   */
/* ------------------------------------------------------------------ */

.content-width {
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: 0 var(--space-6);
}

.section-title {
  font: var(--text-role-section);
  color: var(--color-ink);
  letter-spacing: var(--tracking-wide);
}

/* ShareAnApp's .recent block, with the new-app band above it absent: the
   band's top padding is what put space between the bar and this row. */
/* The same space below the last row as above the first. With the phone's
   sticky ad, the hub script adds the ad's height to the body as well, so
   the last card always scrolls fully into view above it. */
.recent {
  padding-top: var(--space-6);
  padding-bottom: calc(var(--space-6) + env(safe-area-inset-bottom, 0px));
}

.recent-head {
  position: sticky;
  top: var(--topbar-height);
  z-index: var(--z-sticky-row);
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--color-bg-page);
  padding: var(--space-2) 0;
  margin-bottom: var(--space-2);
}

.recent-head::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100vw;
  background: var(--color-bg-page);
  z-index: -1;
}

.recent-head::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: 100vw;
  height: var(--hairline-width);
  background: var(--color-border-hairline);
  opacity: 0;
  transition: opacity var(--motion-fast) var(--motion-ease);
}

.recent-head.is-stuck::after {
  opacity: 1;
  box-shadow: var(--sticky-divider-elevation);
}

.recent-controls {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.recent-controls .icon-button {
  color: var(--color-ink-tertiary);
}

/* Most played stays hidden until its counts load. */
.recent-controls .icon-button[hidden] {
  display: none;
}

.recent-controls .icon-button:hover {
  color: var(--color-ink);
}

.recent-controls .icon-button-current,
.recent-controls .icon-button-current:hover {
  background: var(--color-accent-tint);
  color: var(--color-accent-ink-strong);
}

/* The Starred section sits above Games with the same gap the games row
   keeps below the top bar. */
.starred[hidden] {
  display: none;
}

.starred {
  padding-bottom: var(--space-6);
}

/* Same row height as the Games head, which holds the sort buttons. */
.starred .recent-head {
  min-height: calc(var(--height-button) + 2 * var(--space-2));
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--card-width), 1fr));
  gap: var(--grid-gap);
}

.app-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--card-bg);
  border: var(--hairline-width) solid var(--card-border);
  border-radius: var(--card-radius);
  box-shadow: var(--card-elevation-rest);
  overflow: hidden;
  cursor: pointer;
  color: var(--color-ink);
  transition: var(--transition-hover);
}

.app-card:hover {
  color: var(--color-ink);
}

/* Each game card sits in a cell with its star. The star is a sibling of
   the link rather than inside it, so the cell carries the hover. */
.card-cell {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.card-cell > .app-card {
  flex: 1;
}

@media (hover: hover) and (pointer: fine) {
  .app-card:hover,
  .card-cell:hover > .app-card {
    border-color: var(--card-border-hover);
    box-shadow: var(--card-elevation-hover);
  }
}

.app-card:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* Star affordance, ported from ShareAnApp's .card-star: top-right of the
   cover, shown on hover where there is a pointer, always shown on touch,
   and kept visible with the accent when starred. ShareAnApp measures from
   inside the card's border; the cell is the card's outer edge, so the
   offset adds the hairline back. */
.card-star {
  position: absolute;
  top: calc(var(--space-2) + var(--hairline-width));
  right: calc(var(--space-2) + var(--hairline-width));
  display: grid;
  place-items: center;
  width: var(--space-7);
  height: var(--space-7);
  border-radius: var(--radius-circle);
  background: var(--color-surface);
  border: var(--hairline-width) solid var(--color-border-hairline);
  color: var(--color-ink-secondary);
  opacity: 1;
  transition: var(--transition-hover), opacity var(--motion-fast) var(--motion-ease);
}

@media (hover: hover) and (pointer: fine) {
  .card-star {
    opacity: 0;
  }
}

.card-cell:hover .card-star,
.card-star:focus-visible {
  opacity: 1;
}

.card-star:hover {
  color: var(--color-ink);
  box-shadow: var(--btn-elevation-hover);
}

.card-star:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.card-star-on {
  opacity: 1;
  color: var(--color-accent-ink);
  border-color: var(--color-accent-tint-strong);
}

.card-star-on:hover {
  color: var(--color-accent-ink-strong);
}

/* The cover. ShareAnApp's thumb box, holding the game's icon centred
   where a wireframe preview would sit. */
.thumb {
  height: var(--card-thumb-height);
  background: var(--thumb-bg);
  border-bottom: var(--hairline-width) solid var(--color-border-hairline);
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.thumb-icon {
  align-items: center;
  justify-content: center;
}

.thumb-icon svg {
  width: var(--space-16);
  height: var(--space-16);
}

/* A screenshot of the game mid-round, full bleed, as ShareAnApp's live
   snapshot fills its thumb. The map keeps its own colours in both themes. */
.thumb-cover {
  position: relative;
  padding: 0;
  overflow: hidden;
}

.thumb-cover img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The fallback when a game has no icon: the first letter on a tile the
   same size and corner as the icon, in one of the avatar hues. */
.thumb-letter {
  display: grid;
  place-items: center;
  width: var(--space-16);
  height: var(--space-16);
  /* The icon tile corner: rx 14 on a 64 unit tile. */
  border-radius: calc(var(--space-16) * 14 / 64);
  color: var(--color-on-accent);
  font: var(--text-role-heading);
  font-weight: var(--weight-medium);
}

.thumb-letter.avatar-hue-1 { background: var(--color-avatar-1); }
.thumb-letter.avatar-hue-2 { background: var(--color-avatar-2); }
.thumb-letter.avatar-hue-3 { background: var(--color-avatar-3); }
.thumb-letter.avatar-hue-4 { background: var(--color-avatar-4); }

.app-card-info {
  padding: var(--space-3) var(--space-3) var(--space-2);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.app-card-nameline {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

/* The game's icon on its own tile, left of the name. */
.app-card-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: var(--space-6);
  height: var(--space-6);
}

.app-card-icon svg {
  width: 100%;
  height: 100%;
}

.app-card-icon .thumb-letter {
  width: 100%;
  height: 100%;
  /* The icon tile corner: rx 14 on a 64 unit tile. */
  border-radius: calc(var(--space-6) * 14 / 64);
  font: var(--text-role-meta);
  font-weight: var(--weight-medium);
}

.app-card-name {
  font: var(--text-role-card-name);
  color: var(--color-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The tagline takes the meta row's slot, so every card is one height
   whether or not it has a tagline. */
.app-card-meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: calc(var(--text-sm) * var(--leading-normal));
  color: var(--color-ink-tertiary);
  font: var(--text-role-meta);
}

.app-card-tagline {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.empty-note {
  padding: var(--space-8) 0;
  color: var(--color-ink-tertiary);
  text-align: center;
}

@media (max-width: 620px) {
  /* Sides only: the shorthand would zero the top and bottom padding that
     .recent sets. */
  .content-width {
    padding-left: var(--space-4);
    padding-right: var(--space-4);
  }

  .card-grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 380px) {
  .recent-head {
    flex-wrap: wrap;
    gap: var(--space-2);
  }

  .recent-controls {
    width: 100%;
    justify-content: flex-start;
    flex-wrap: wrap;
  }
}

/* ------------------------------------------------------------------ */
/* Footer: theme, language, and the mark                                */
/* ------------------------------------------------------------------ */

/* Below the last card and above the phone's sticky ad, which the hub
   script reserves as body padding. Shown at every width; on desktop only
   the brand and legal links show, since the top bar keeps the controls. */
.hub-foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: calc(var(--space-6) + env(safe-area-inset-bottom, 0px));
}

/* The hairline sits inside the side padding, so it spans exactly the card
   grid; with the column gap it leaves --space-6 above the controls. */
.hub-foot::before {
  content: "";
  align-self: stretch;
  height: var(--hairline-width);
  margin-bottom: var(--space-3);
  background: var(--color-border-hairline);
}

/* One row, wrapping to two on a narrow screen. */
.hub-foot-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
}

.hub-foot-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font: var(--text-role-meta);
  color: var(--color-ink-tertiary);
}

.hub-foot-brand svg {
  flex: none;
}

/* The brand and the Terms and Privacy links share one small line, which
   wraps under the brand on a narrow screen. */
.hub-foot-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-2) var(--space-4);
}

.hub-foot-brand a,
.hub-foot-links a {
  color: var(--color-ink-tertiary);
  border-radius: var(--radius-sm);
  transition: var(--transition-hover);
}

.hub-foot-links {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  font: var(--text-role-meta);
}

.hub-foot-brand a:hover,
.hub-foot-links a:hover {
  color: var(--color-ink);
  text-decoration: underline;
}

.hub-foot-links a[aria-current="page"] {
  color: var(--color-ink-secondary);
}

.hub-foot-brand a:focus-visible,
.hub-foot-links a:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* ------------------------------------------------------------------ */
/* Ad slots (docs/ADS.md). Hidden until the hub script fills a key.    */
/* ------------------------------------------------------------------ */

.hub-ad-top[hidden],
.ad-card[hidden],
.ad-sticky[hidden] {
  display: none;
}

.ad-slot {
  display: flex;
  align-items: center;
  justify-content: center;
  max-width: 100%;
  overflow: hidden;
}

/* The 728x90 under the top bar. Its top padding matches the gap the games
   row already keeps below it, so hiding it leaves no extra space. */
.hub-ad-top {
  display: flex;
  justify-content: center;
  padding-top: var(--space-6);
}

/* The in-grid unit wears the card's own radius, border, and fill. It is
   not a link, so it has no hover lift. */
.ad-card {
  align-items: center;
  justify-content: center;
  padding: var(--space-6) 0 var(--space-3);
  overflow: hidden;
  cursor: default;
}

@media (hover: hover) and (pointer: fine) {
  .ad-card:hover {
    border-color: var(--card-border);
    box-shadow: var(--card-elevation-rest);
  }
}


.ad-label {
  position: absolute;
  top: var(--space-1);
  right: var(--space-2);
  font: var(--text-role-meta);
  color: var(--color-ink-tertiary);
}

/* The phone footer. The close tab sits above the bar, clear of the unit,
   and the page reserves both heights so no content hides behind them. */
.ad-sticky {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-sticky);
  display: flex;
  justify-content: center;
  padding: var(--space-2) var(--space-4) calc(var(--space-1) + env(safe-area-inset-bottom, 0px));
  background: var(--color-bg-page);
  border-top: var(--hairline-width) solid var(--color-border-hairline);
}

.ad-close {
  position: absolute;
  bottom: 100%;
  right: var(--space-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--space-8);
  height: var(--space-6);
  background: var(--color-bg-page);
  border: var(--hairline-width) solid var(--color-border-hairline);
  border-bottom: none;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  color: var(--color-ink-secondary);
}

.ad-close:hover {
  color: var(--color-ink);
}

.ad-close:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

@media (min-width: 720px) {
  .ad-sticky {
    display: none;
  }
}

/* The theme and language controls are a phone thing: desktop keeps them in
   the top bar. The footer line with the Terms and Privacy links shows at
   every width. */
@media (min-width: 621px) {
  .hub-foot-controls { display: none; }
}
