/* ==========================================================================
   EzyGamers — games listing, category & player page styles
   ========================================================================== */

/* Page header ------------------------------------------------------------- */
.page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  margin-bottom: 20px;
}
.page-head h1 { font-size: clamp(24px, 3vw, 34px); font-weight: 900; letter-spacing: -.035em; }
.page-head p { color: var(--muted); margin: 6px 0 0; }
.crumbs { font-size: 12px; color: var(--muted-dim); margin-bottom: 8px; }
/* Breadcrumb links were 16px tall - under the 24px minimum for a pointer
   target. The padding makes the target big enough without moving the text. */
.crumbs a { display: inline-block; padding: 5px 2px; }
.crumbs a:hover { color: var(--cyan); }
.crumbs span { margin: 0 4px; }

/* Toolbar ----------------------------------------------------------------- */
.toolbar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 14px;
  border-radius: var(--radius);
  background: var(--bg-2);
  border: 1px solid var(--line);
  margin-bottom: 18px;
}
.field { position: relative; flex: 1 1 260px; min-width: 200px; }
.field input {
  width: 100%; height: 40px;
  padding: 0 14px 0 38px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: #171927; color: #fff; outline: none;
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
.field input:focus { border-color: var(--purple); box-shadow: 0 0 0 4px rgba(108, 76, 255, .16); }
.field input::placeholder { color: var(--muted-dim); }
.field__ico { position: absolute; left: 12px; top: 11px; color: var(--muted-dim); }
.field__ico svg { width: 18px; height: 18px; }

.select {
  height: 40px;
  padding: 0 34px 0 14px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: #171927 url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%239aa0b5' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") no-repeat right 11px center;
  color: #fff;
  appearance: none; -webkit-appearance: none;
  outline: none; cursor: pointer;
}
.select:focus { border-color: var(--purple); }

.filters { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 20px; }
.filter {
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: rgba(255, 255, 255, .04);
  color: var(--muted);
  font-size: 13px; font-weight: 600;
  white-space: nowrap;
  transition: all .18s var(--ease);
}
.filter:hover { color: #fff; border-color: var(--purple-bright); }
.filter.is-active {
  background: var(--grad);
  color: #0b0c14;
  border-color: transparent;
  box-shadow: 0 6px 18px rgba(108, 76, 255, .38);
}

.result-count { color: var(--muted); font-size: 14px; margin-bottom: 14px; }
.load-more-wrap { display: grid; place-items: center; margin-top: 28px; }

/* ==========================================================================
   Play page
   ========================================================================== */
.play-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 20px;
  align-items: start;
}

.stage {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--line-strong);
  background: #05060c;
  box-shadow: var(--shadow);
}
.stage__frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  background: #05060c;
}

/* Give the game the screen.
   A fixed 16:10 box left the game at roughly half the viewport on a laptop.
   On anything above phone width the stage is driven by viewport height
   instead, so the playable area fills the space the way a game platform
   should, while the clamp keeps it sane on very short and very tall screens.
   Phones keep the aspect-ratio rule below, where height is the scarce axis. */
@media (min-width: 761px) {
  .stage__frame {
    aspect-ratio: auto;
    height: clamp(440px, calc(100vh - var(--header-h) - 152px), 940px);
  }
}

/* The right rail only sits beside the game when the screen is wide enough to
   afford it. Below that it moves underneath, so the game always gets the full
   content width — otherwise a 1600px laptop gave the game LESS room than a
   1440px one. Under 1200px responsive.css hides the rail entirely and
   .recs-mobile takes over. */
@media (max-width: 1699px) {
  .play-layout { grid-template-columns: minmax(0, 1fr); }
  .rail-col {
    position: static;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-start;
  }
  .rail-col > * { flex: 1 1 320px; min-width: 0; }
}
/* Portrait games. js/player.js adds this for games whose own layout is a
   fixed-width phone column; see the note there for why widening the frame
   makes those worse rather than better. The frame narrows to the game and
   the stage's own background fills what is left, so it reads as a deliberate
   portrait cabinet instead of a phone adrift in a landscape box.

   Only above 760px: a phone's stage is already narrower than this. */
@media (min-width: 761px) {
  .stage--portrait .stage__frame {
    max-width: min(560px, 100%);
    margin-inline: auto;
    /* A rim and a cast shadow, so the edge where the game meets the backdrop
       reads as the side of a cabinet rather than a seam between two greys. */
    box-shadow: 0 0 0 1px rgba(255, 255, 255, .07), 0 24px 64px rgba(0, 0, 0, .55);
  }
}
/* Fullscreen needs the cap most of all: without it the game's own column sits
   at 448px in the middle of a 1920px screen. With it, the game gets a full
   height portrait strip, which is the shape it was drawn for. */
.stage--portrait:fullscreen .stage__frame,
.stage--portrait:-webkit-full-screen .stage__frame {
  max-width: min(560px, 100%);
  margin-inline: auto;
  /* A rim and a cast shadow, so the edge where the game meets the backdrop
     reads as the side of a cabinet rather than a seam between two greys. */
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .07), 0 24px 64px rgba(0, 0, 0, .55);
}

/* What sits beside a portrait game -----------------------------------------
   Narrowing the frame leaves ~300px a side on a desktop. Flat black there
   looked like a loading failure, so it carries the game's own art instead,
   blurred well past recognition and dimmed - present enough to be a backdrop,
   quiet enough that the game is still the only thing you look at. The same
   idea as .fslide__art in the carousel.

   --stage-art is set per game by js/player.js. Without it the rule paints
   nothing and the stage falls back to its own background. */
.stage--portrait::before,
.stage--portrait::after {
  content: "";
  position: absolute;
  z-index: 0;
  pointer-events: none;
}
.stage--portrait::before {
  /* Overscanned: a blur needs material beyond the edges or it fades out into
     the corners. */
  inset: -60px;
  background: var(--stage-art, none) center / cover no-repeat;
  filter: blur(54px) saturate(.8);
  opacity: .4;
}
.stage--portrait::after {
  inset: 0;
  background: radial-gradient(64% 74% at 50% 44%, rgba(5, 6, 12, .2), rgba(5, 6, 12, .82));
}
/* Both pseudo-elements are behind the game and its toolbar. */
.stage--portrait .stage__frame,
.stage--portrait .stage__bar { position: relative; z-index: 1; }

.stage__frame iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0;
  display: block;
}
.stage:fullscreen, .stage:-webkit-full-screen { border-radius: 0; border: 0; }
.stage:fullscreen .stage__frame, .stage:-webkit-full-screen .stage__frame { aspect-ratio: auto; height: 100vh; }
.stage:fullscreen .stage__bar, .stage:-webkit-full-screen .stage__bar { position: absolute; bottom: 0; left: 0; right: 0; background: rgba(12, 13, 22, .92); }

/* Loading splash */
.stage__splash {
  position: absolute; inset: 0; z-index: 3;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 16px;
  background: #05060c;
  transition: opacity .35s var(--ease), visibility .35s;
}
.stage__splash.is-hidden { opacity: 0; visibility: hidden; }
.stage__splash-text {
  margin: 0;
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--muted);
  text-align: center;
  padding: 0 24px;
}
.spinner {
  width: 46px; height: 46px; border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, .12);
  border-top-color: var(--purple-bright);
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* A spinner that never stops is worse than no spinner, so the splash is
   always resolved — into the game, or into this. */
.stage__error {
  position: absolute; inset: 0; z-index: 4;
  display: grid; place-items: center;
  padding: 24px;
  background: radial-gradient(120% 90% at 50% 0%, #141527 0%, #05060c 68%);
  overflow-y: auto;
}
.stage__error[hidden] { display: none; }
.stage__error-box { max-width: 460px; text-align: center; }
.stage__error-emoji { font-size: 42px; line-height: 1; margin-bottom: 10px; }
.stage__error-title {
  margin: 0 0 8px;
  font-size: 21px;
  font-weight: 800;
  letter-spacing: -.02em;
}
.stage__error-msg {
  margin: 0;
  font-size: 14px;
  line-height: 1.55;
  color: var(--muted);
}
.stage__error-detail {
  margin: 12px 0 0;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, .04);
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--muted-dim);
  text-align: left;
  word-break: break-word;
  max-height: 120px;
  overflow-y: auto;
}
.stage__error-detail[hidden] { display: none; }
.stage__error-actions {
  display: flex; flex-wrap: wrap;
  gap: 10px; justify-content: center;
  margin-top: 18px;
}

@media (max-width: 520px) {
  .stage__error-title { font-size: 18px; }
  .stage__error-actions .btn { flex: 1 1 100%; justify-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2.4s; }
  .stage__splash { transition: none; }
}

.stage__bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 14px;
  background: #12131e;
  border-top: 1px solid var(--line);
}
.stage__id { display: flex; align-items: center; gap: 10px; margin-right: auto; min-width: 0; }
.stage__id img { width: 40px; height: 24px; border-radius: 6px; object-fit: cover; }
.stage__name { font-weight: 700; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.tool {
  display: inline-flex; align-items: center; gap: 6px;
  height: 36px; padding: 0 13px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, .04);
  color: var(--muted);
  font-size: 13px; font-weight: 600;
  transition: all .16s var(--ease);
}
.tool:hover { color: #fff; background: rgba(255, 255, 255, .1); }
.tool.is-on { color: var(--cyan); border-color: rgba(34, 211, 238, .45); background: rgba(34, 211, 238, .12); }
.tool.is-on--like { color: var(--green); border-color: rgba(34, 197, 94, .45); background: rgba(34, 197, 94, .12); }
.tool.is-on--down { color: #f87171; border-color: rgba(248, 113, 113, .45); background: rgba(248, 113, 113, .12); }
.tool svg { width: 17px; height: 17px; }

/* Info panels */
.panel {
  margin-top: 18px;
  padding: 20px 22px;
  border-radius: var(--radius);
  background: var(--bg-2);
  border: 1px solid var(--line);
}
.panel h2 { font-size: 20px; margin-bottom: 8px; }
.panel h3 { font-size: 15px; margin: 18px 0 8px; color: var(--cyan); }
.panel p { color: var(--muted); margin: 0 0 8px; }
.panel ol, .panel ul { color: var(--muted); padding-left: 20px; margin: 0; }
.panel li { margin-bottom: 6px; list-style: disc; }
.panel ol li { list-style: decimal; }
.tags { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 16px; }
.tag {
  padding: 5px 12px; border-radius: 999px;
  background: rgba(108, 76, 255, .14);
  border: 1px solid rgba(108, 76, 255, .3);
  color: #c9bcff; font-size: 12px; font-weight: 600;
}
.tag:hover { background: rgba(108, 76, 255, .28); }

.stats-row { display: flex; gap: 20px; flex-wrap: wrap; margin-top: 14px; }
.stat__val { font-size: 20px; font-weight: 900; }
.stat__key { font-size: 11.5px; color: var(--muted-dim); text-transform: uppercase; letter-spacing: .1em; }

/* ==========================================================================
   Controls panel — the toolbar's "Controls" button.

   Scoped to the stage frame rather than the page: it covers the game, leaves
   the toolbar underneath reachable so the same button dismisses it, and comes
   along when the stage goes fullscreen. Below 760px the stage frame is only a
   couple of hundred pixels tall, so it becomes a full-viewport sheet instead.
   Follows the same open/close conventions as the auth scrim.
   ========================================================================== */
.cmodal {
  position: absolute;
  inset: 0;
  z-index: 8;                 /* above the start veil, which sits at 6 */
  display: grid;
  place-items: center;
  padding: 18px;
  background: rgba(5, 6, 12, .82);
  backdrop-filter: blur(5px);
  opacity: 0;
  transition: opacity .2s var(--ease);
}
.cmodal[hidden] { display: none; }
.cmodal.is-open { opacity: 1; }

.cmodal__card {
  width: min(560px, 100%);
  max-height: 100%;
  display: flex;
  flex-direction: column;
  border-radius: var(--radius);
  background:
    radial-gradient(560px 200px at 50% 0%, rgba(108, 76, 255, .26), transparent 64%),
    linear-gradient(165deg, #1d1f2f, #131420);
  border: 1px solid var(--line-strong);
  box-shadow: 0 30px 70px rgba(0, 0, 0, .66);
  transform: translateY(12px) scale(.975);
  transition: transform .22s var(--ease);
  overflow: hidden;
}
.cmodal.is-open .cmodal__card { transform: none; }

.cmodal__head {
  display: flex; align-items: center; gap: 12px;
  padding: 15px 16px 13px 18px;
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}
.cmodal__thumb {
  width: 46px; height: 30px;
  border-radius: 7px;
  object-fit: cover;
  border: 1px solid var(--line-strong);
  flex: 0 0 auto;
}
.cmodal__ident { min-width: 0; margin-right: auto; }
.cmodal__title { font-size: 16px; font-weight: 800; letter-spacing: -.01em; margin: 0; }
.cmodal__sub {
  margin: 1px 0 0;
  font-size: 12.5px;
  color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cmodal__close {
  flex: 0 0 auto;
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: 9px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, .05);
  color: var(--muted);
  font-size: 14px; line-height: 1;
  transition: all .16s var(--ease);
}
.cmodal__close:hover { color: #fff; background: rgba(255, 255, 255, .12); }

.cmodal__body {
  padding: 6px 18px 18px;
  overflow-y: auto;
  overscroll-behavior: contain;
  flex: 1 1 auto;
}
.cmodal__body .ctrls { gap: 14px; }
.cmodal__body .ctrls__group h3 { margin: 14px 0 2px; font-size: 13px; }
.cmodal__body .ctrl-row { grid-template-columns: minmax(110px, 168px) 1fr; }
.cmodal__body p.muted { margin: 14px 0 4px; line-height: 1.6; }

/* Full-viewport sheet where the stage frame is too short to hold a panel.
   It has to clear the fixed site header (z-index 60) and sidebar (50), or the
   card's own header — and its close button — ends up underneath them. It
   still sits below the auth scrim at 200, so signing in from here layers
   correctly. */
@media (max-width: 760px) {
  .cmodal { position: fixed; padding: 0; z-index: 120; }
  .cmodal__card {
    width: 100%;
    height: 100%;
    max-height: 100%;
    border-radius: 0;
    border: 0;
  }
  .cmodal__body { padding: 6px 16px 24px; }
}

@media (prefers-reduced-motion: reduce) {
  .cmodal, .cmodal__card { transition: none; }
}

/* ==========================================================================
   Game detail block — the identity, tabs and content under the stage.
   Shares .panel's surface treatment; everything else is specific to it.
   ========================================================================== */
.ginfo {
  margin-top: 18px;
  border-radius: var(--radius);
  background: var(--bg-2);
  border: 1px solid var(--line);
  overflow: hidden;
}

.ginfo__head {
  display: flex; gap: 16px; align-items: flex-start;
  padding: 20px 22px 16px;
}
.ginfo__cover {
  width: 132px; height: 84px;
  flex: 0 0 auto;
  object-fit: cover;
  border-radius: var(--radius-card);
  border: 1px solid var(--line-strong);
  background: var(--card);
}
.ginfo__ident { min-width: 0; flex: 1 1 auto; }
.ginfo__badges { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 6px; }
.ginfo__badges:empty { display: none; }
.ginfo__title {
  font-size: clamp(21px, 3vw, 27px);
  font-weight: 900;
  letter-spacing: -.02em;
  line-height: 1.15;
  margin: 0;
}
.ginfo__tagline {
  color: var(--cyan);
  font-weight: 600;
  font-size: 14px;
  margin: 5px 0 0;
}
.ginfo__tagline:empty { display: none; }
.ginfo__meta {
  display: flex; flex-wrap: wrap;
  gap: 6px 8px;
  margin-top: 10px;
  font-size: 12.5px;
  color: var(--muted);
}
/* The category link here is a tap target as well as a label. */
.ginfo__meta a { color: var(--muted); display: inline-block; padding: 3px 2px; }
.ginfo__meta a:hover { color: var(--text); }
.ginfo__meta i {
  font-style: normal;
  color: var(--muted-dim);
  user-select: none;
}
.ginfo .stats-row { padding: 0 22px 4px; margin-top: 0; }

/* "Available on Android" ---------------------------------------------------
   Only a game with a native build has one, so this is never the page's main
   action. It is a calm full-width callout under the game's heading: room
   enough to say what the offer is, quiet enough that nothing about it
   competes with the game already running above it. */
.store-cta {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin: 16px 22px 4px;
  padding: 14px 16px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background:
    linear-gradient(135deg, rgba(0, 211, 255, .06), rgba(0, 240, 118, .05)),
    rgba(255, 255, 255, .02);
}
.store-cta[hidden] { display: none; }
.store-cta__ico {
  display: inline-flex;
  align-items: center; justify-content: center;
  width: 44px; height: 44px;
  flex: none;
  border-radius: 12px;
  background: rgba(255, 255, 255, .06);
  border: 1px solid var(--line);
}
.store-cta__ico .play-mark { width: 22px; height: 24px; }
.store-cta__copy { flex: 1 1 240px; min-width: 0; }
.store-cta__copy strong {
  display: block;
  font-size: 14.5px; font-weight: 800;
  letter-spacing: -.01em;
}
.store-cta__copy p {
  margin: 3px 0 0;
  font-size: 12.5px; line-height: 1.5;
  color: var(--muted);
}
/* The action. Distinct from "Play" by colour and by the mark it carries, so
   the two are never mistaken for each other. */
.store-cta__btn {
  display: inline-flex; align-items: center; gap: 9px;
  flex: none;
  /* Height comes from .btn in css/portal.css, which every button shares. */
  padding: 0 18px;
  white-space: nowrap;
}
.store-cta__btn .play-mark { width: 16px; height: 17px; }

/* Below the tablet breakpoint the button takes the full width of the callout
   rather than crushing against the copy - the one place an install offer is
   most likely to be wanted is the one where it has least room. */
@media (max-width: 620px) {
  .store-cta { margin-inline: 14px; gap: 12px; }
  .store-cta__copy { flex-basis: 100%; }
  .store-cta__btn { flex: 1 1 100%; justify-content: center; }
}

/* The marker on a game card ------------------------------------------------
   Not a button: a card is one <a>, and a link inside a link is invalid HTML
   and unreachable by keyboard. This says the app exists; the preview the
   card opens is where you can act on it. Opposite corner from the badge. */
.gcard__store {
  position: absolute;
  top: 10px; right: 10px;
  z-index: 2;
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 8px 4px 6px;
  border-radius: 7px;
  font-size: 9.5px; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase;
  color: #eaf2ff;
  background: rgba(8, 11, 20, .72);
  border: 1px solid rgba(255, 255, 255, .14);
  backdrop-filter: blur(4px);
  pointer-events: none;
}
.gcard__store .play-mark { width: 11px; height: 12px; }

/* On the start veil, under the Play hint ---------------------------------
   A quiet outline, well below the Play button and much smaller than it: the
   point is that it can be found without scrolling, not that it competes with
   playing. */
.veil__store {
  display: inline-flex; align-items: center; gap: 8px;
  margin-top: 16px;
  padding: 8px 15px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: rgba(8, 11, 20, .55);
  color: var(--text);
  font-size: 12.5px; font-weight: 700;
  text-decoration: none;
  transition: border-color .18s var(--ease), background .18s var(--ease),
              transform .18s var(--ease);
}
.veil__store[hidden] { display: none; }
.veil__store:hover {
  border-color: rgba(255, 255, 255, .34);
  background: rgba(8, 11, 20, .8);
  transform: translateY(-1px);
}
.veil__store:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }
.veil__store .play-mark { width: 14px; height: 15px; }

/* The hero strip's "To install" figure, once a game has a native build. */
.hero__stat a { color: inherit; text-decoration: none; border-bottom: 1px solid currentColor; }
.hero__stat a:hover { color: var(--cyan); }

@media (prefers-reduced-motion: reduce) {
  .veil__store, .veil__store:hover { transition: none; transform: none; }
}

/* The preview modal's action, beside Play Now. */
.gpv__store[hidden] { display: none; }
.gpv__store {
  display: inline-flex; align-items: center; gap: 8px;
  white-space: nowrap;
}
.gpv__store .play-mark { width: 15px; height: 16px; }

@media (prefers-reduced-motion: reduce) {
  .store-cta__btn, .gpv__store { transition: none; }
}

/* Tabs -------------------------------------------------------------------- */
.tabs {
  display: flex;
  gap: 2px;
  margin-top: 16px;
  padding: 0 12px;
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs__btn {
  position: relative;
  flex: 0 0 auto;
  padding: 12px 14px;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font: inherit;
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  transition: color .18s var(--ease), border-color .18s var(--ease);
}
.tabs__btn:hover { color: var(--text); }
.tabs__btn.is-active { color: var(--text); border-bottom-color: var(--purple-bright); }
.tabs__btn:focus-visible { outline: 2px solid var(--cyan); outline-offset: -4px; border-radius: 4px; }

.tabs__panel { padding: 18px 22px 4px; }
.tabs__panel[hidden] { display: none; }
.tabs__panel h3 { font-size: 14px; margin: 18px 0 9px; color: var(--cyan); }
.tabs__panel p { color: var(--muted); margin: 0 0 8px; }
.tabs__panel:focus-visible { outline: 2px solid var(--cyan); outline-offset: -6px; border-radius: 8px; }

.ginfo__desc { line-height: 1.65; max-width: 74ch; }

/* One-line objective, lifted out of the prose so it reads at a glance. */
.callout {
  display: flex; gap: 12px; align-items: flex-start;
  margin: 14px 0 0;
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(108, 76, 255, .3);
  background: rgba(108, 76, 255, .1);
}
.callout[hidden] { display: none; }
.callout__k {
  flex: 0 0 auto;
  font-size: 10.5px; font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase;
  color: #c9bcff;
  padding-top: 3px;
}
.callout__v { margin: 0 !important; color: var(--text) !important; font-size: 13.5px; line-height: 1.55; }

/* Numbered How-to-Play steps */
.steps { list-style: none; padding: 0; margin: 0; counter-reset: step; display: grid; gap: 10px; }
.steps li {
  list-style: none;
  counter-increment: step;
  display: grid;
  grid-template-columns: 26px 1fr;
  gap: 12px;
  align-items: start;
  margin: 0;
  color: var(--muted);
  line-height: 1.55;
}
.steps li::before {
  content: counter(step);
  display: grid; place-items: center;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: rgba(108, 76, 255, .16);
  border: 1px solid rgba(108, 76, 255, .34);
  color: #c9bcff;
  font-size: 12px; font-weight: 800;
}

.ticks { list-style: none; padding: 0; margin: 0; display: grid; gap: 8px; }
.ticks li {
  list-style: none;
  margin: 0;
  padding-left: 22px;
  position: relative;
  color: var(--muted);
  line-height: 1.55;
  font-size: 13.5px;
}
.ticks li::before {
  content: '›';
  position: absolute; left: 6px; top: -1px;
  color: var(--purple-bright);
  font-weight: 900;
}

/* Controls ---------------------------------------------------------------- */
.ctrls { display: grid; gap: 18px; }
.ctrls__group h3 { margin-top: 0 !important; }
.ctrl-row {
  display: grid;
  grid-template-columns: minmax(120px, 200px) 1fr;
  gap: 12px 16px;
  align-items: center;
  padding: 9px 0;
  border-bottom: 1px solid var(--line);
}
.ctrl-row:last-child { border-bottom: 0; }
.ctrl-keys { display: flex; gap: 5px; flex-wrap: wrap; align-items: center; }
.kbd {
  display: inline-grid; place-items: center;
  min-width: 30px; height: 28px;
  padding: 0 8px;
  border-radius: 7px;
  background: linear-gradient(180deg, var(--card-2), var(--card));
  border: 1px solid var(--line-strong);
  border-bottom-width: 2px;
  color: var(--text);
  font-size: 12.5px; font-weight: 700;
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
}
.kbd--wide { min-width: auto; }
.ctrl-act { color: var(--muted); font-size: 13.5px; line-height: 1.5; }
.ctrl-act--full { grid-column: 1 / -1; }
.ctrl-note {
  margin-top: 4px !important;
  font-size: 12.5px;
  color: var(--muted-dim) !important;
  font-style: italic;
}

/* Levels ------------------------------------------------------------------ */
.levels { display: grid; gap: 8px; }
.level-row {
  display: grid;
  grid-template-columns: 44px 1fr auto;
  gap: 14px;
  align-items: center;
  padding: 11px 14px;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, .03);
  border: 1px solid var(--line);
}
.level-n {
  display: grid; place-items: center;
  width: 44px; height: 30px;
  border-radius: 8px;
  background: rgba(108, 76, 255, .16);
  border: 1px solid rgba(108, 76, 255, .32);
  color: #c9bcff;
  font-size: 12px; font-weight: 800;
  letter-spacing: .04em;
}
.level-body { min-width: 0; }
.level-label { font-size: 13.5px; font-weight: 600; }
.level-sub { font-size: 11.5px; color: var(--muted-dim); margin-top: 2px; }
.level-target { font-size: 12.5px; color: var(--muted); white-space: nowrap; text-align: right; }
.level-target b { display: block; color: var(--gold); font-size: 14px; font-weight: 800; }

.diff { font-size: 10.5px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; }
.diff--easy { color: var(--green); }
.diff--medium { color: var(--gold); }
.diff--hard { color: var(--pink); }

/* Tablet / mobile --------------------------------------------------------- */
@media (max-width: 760px) {
  .ginfo__head { padding: 16px 16px 12px; gap: 13px; }
  .ginfo__cover { width: 96px; height: 62px; }
  .ginfo .stats-row { padding: 0 16px 4px; gap: 16px; }
  .tabs { padding: 0 6px; margin-top: 13px; }
  .tabs__btn { padding: 11px 11px; font-size: 13px; }
  .tabs__panel { padding: 15px 16px 4px; }
  .ctrl-row { grid-template-columns: 1fr; gap: 7px; padding: 11px 0; }
  .level-row { grid-template-columns: 40px 1fr; gap: 11px; padding: 10px 12px; }
  .level-target { grid-column: 2; text-align: left; }
  .level-target b { display: inline; font-size: 13px; }
}

@media (max-width: 460px) {
  .ginfo__head { flex-direction: column; }
  .ginfo__cover { width: 100%; height: 128px; }
}

/* Right rail -------------------------------------------------------------- */
.rail-col { display: flex; flex-direction: column; gap: 16px; position: sticky; top: calc(var(--header-h) + 18px); }
.card-panel {
  border-radius: var(--radius);
  background: var(--bg-2);
  border: 1px solid var(--line);
  overflow: hidden;
}
.card-panel__head {
  padding: 13px 16px;
  border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.card-panel__title { font-size: 12px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
/* When the panel title is a link it is also a tap target, and 18px tall is
   not one. */
a.card-panel__title { display: inline-block; padding: 5px 2px; }
a.card-panel__title:hover { color: #fff; }
.card-panel__body { padding: 12px; }
.card-panel__body .lb-row { grid-template-columns: 24px 28px 1fr auto; padding: 7px 8px; gap: 8px; }
.card-panel__body .lb-ava { width: 28px; height: 28px; font-size: 11.5px; }
.card-panel__body .lb-name { font-size: 13px; }

.mini-list { display: flex; flex-direction: column; gap: 10px; }
.mini {
  display: flex; gap: 10px; align-items: center;
  padding: 6px; border-radius: 12px;
  transition: background .16s var(--ease);
}
.mini:hover { background: rgba(255, 255, 255, .06); }
.mini img { width: 84px; height: 48px; object-fit: cover; border-radius: 8px; flex: 0 0 auto; }
/* The title and its meta line are separate rows. Without this they are inline
   spans and render as "Quick SudokuPuzzle Games" with no break between them. */
.mini > span { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.mini__t {
  font-size: 14px; font-weight: 650; line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.mini__c {
  font-size: 12px; color: var(--muted); line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.recs-mobile { display: none; }

/* ==========================================================================
   "Want to save your score?"
   --------------------------------------------------------------------------
   The offer a guest gets after a run, drawn by EZY.offerToSaveScore in
   js/main.js and placed directly under the stage. It is part of the page, not
   a layer over it: the game keeps its whole frame, nothing is covered and
   nothing has to be dismissed before playing again.
   ========================================================================== */
.save-offer {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 14px;
  padding: 14px 18px;
  border-radius: var(--radius);
  border: 1px solid rgba(139, 92, 246, .34);
  background:
    radial-gradient(600px 120px at 8% 0%, rgba(108, 76, 255, .2), transparent 70%),
    rgba(108, 76, 255, .08);
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity .24s var(--ease), transform .24s var(--ease);
}
.save-offer.is-in { opacity: 1; transform: none; }

.save-offer__body { display: flex; align-items: flex-start; gap: 12px; flex: 1 1 320px; min-width: 0; }
.save-offer__ico { font-size: 22px; line-height: 1.2; flex: 0 0 auto; }
.save-offer__copy strong { display: block; font-size: 15px; font-weight: 800; letter-spacing: -.01em; }
.save-offer__copy p { margin: 4px 0 0; font-size: 13px; line-height: 1.5; color: var(--muted); }
.save-offer__copy b { color: var(--text); }

.save-offer__actions { display: flex; gap: 8px; flex-wrap: wrap; flex: 0 0 auto; align-items: center; }

/* Declining is a real choice and has to be easy to find, but it is not
   competing with the two beside it. */
.save-offer__no {
  border: 0;
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  padding: 8px 10px;
  border-radius: 999px;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.save-offer__no:hover { color: #fff; background: rgba(255, 255, 255, .06); }

@media (max-width: 760px) {
  .save-offer { padding: 13px 14px; gap: 12px; }
  .save-offer__actions { width: 100%; }
  .save-offer__actions .btn { flex: 1 1 auto; justify-content: center; }
  .save-offer__no { flex: 1 1 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .save-offer { transition: none; }
}
