/* HUB THEME (coachfio.com only) - light, with violet as the accent.
   Separate from app.css on purpose: the hub belongs to no single game, so it has
   its own identity, and each game site keeps its own. FC is dark green; this is
   light violet-accented; a future title brings its own stylesheet and nothing
   here moves.

   The ground is light and the DARK sections are the exception: one violet band
   to break the page, and the closing panel. Violet is an accent here, not the
   surface. Everything else from the reference designs holds - one chromatic
   actor, a pill kicker, the accent used INSIDE the headline rather than around
   it, numbered step cards, alternating bands.

   Deliberately NOT taken from them: invented social proof ("10K+ players",
   "2M+ matches", "4.9/5"), nav links to pages that do not exist, and a
   newsletter box that goes nowhere. Those are what make a page read as
   generated rather than built.

   Restraint rules held throughout:
     - Violet is the only chromatic actor. Everything else is neutral.
     - Glow appears at most once per view, behind the thing being sold.
     - Hover shifts a border or a fill. Nothing lifts, nothing scales. */

:root {
  /* Paper, not near-black. Each step is a real step so cards still separate
     from the page and the footer still separates from both. */
  --h-bg: #F7F7FB;
  --h-bg-2: #FFFFFF;
  --h-surface: #FFFFFF;
  --h-surface-2: #F1F1F6;
  --h-line: rgba(0, 0, 0, .08);
  --h-line-2: rgba(0, 0, 0, .16);
  --h-ink: #14121F;
  --h-ink-2: #4A4660;
  --h-ink-3: #86829C;
  --h-violet: #7C5CFF;
  /* On a light ground the readable accent IS the deep one, so violet-2 (the
     "accent text" role) and violet-deep (the "pressed/hover" role) converge. */
  --h-violet-2: #5B3FD6;
  --h-violet-deep: #5B3FD6;
  --h-violet-soft: rgba(124, 92, 255, .10);
  --h-violet-line: rgba(124, 92, 255, .28);
  --h-bad: #BA1A1A;

  /* The inverse set, used by the two dark sections below. Named rather than
     inlined so the violet band and the closing panel cannot drift apart. */
  --h-dk-bg: #14121F;
  --h-dk-surface: #1E1B33;
  --h-dk-line: rgba(255, 255, 255, .09);
  --h-dk-ink: #F5F4FA;
  --h-dk-ink-2: #A9A6BE;
  --h-dk-accent: #9B84FF;

  /* Glass scale (glassmorphism system): one blur/opacity vocabulary so every
     frosted surface on the page is one of three intensities, not improvised.
     Light glass sits on the light page; dark glass sits on key art. Borders are
     the low-opacity white "edge light" that makes a pane read as glass. */
  --h-glass-blur-sm: 8px; --h-glass-blur-md: 16px; --h-glass-blur-lg: 24px;
  --h-glass-light: rgba(255, 255, 255, .55);
  --h-glass-light-2: rgba(255, 255, 255, .40);
  --h-glass-dark: rgba(10, 8, 20, .32);
  --h-glass-edge: rgba(255, 255, 255, .60);
  --h-glass-edge-dark: rgba(255, 255, 255, .18);

  --h-r: 10px;
  --h-r-lg: 16px;
  --h-s1: 8px; --h-s2: 16px; --h-s3: 24px; --h-s4: 40px; --h-s5: 72px;

  --h-font: "Plus Jakarta Sans", "Inter", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --h-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
}

*, *::before, *::after { box-sizing: border-box; }
/* The UA rule for [hidden] is display:none, but ANY author display wins over it. */
[hidden] { display: none !important; }

.hub {
  margin: 0; background: var(--h-bg); color: var(--h-ink);
  font-family: var(--h-font); font-size: 16px; line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
.hub a { color: inherit; }
.hub-wrap { max-width: 1180px; margin: 0 auto; padding: 0 24px; }
.hub-skip { position: absolute; left: -9999px; }
.hub-skip:focus { left: 24px; top: 24px; z-index: 100; background: var(--h-surface); padding: 10px 16px; border-radius: var(--h-r); }
.hub :focus-visible { outline: 2px solid var(--h-violet); outline-offset: 3px; border-radius: var(--h-r); }

/* ---- top bar ------------------------------------------------------------ */
.hub-top {
  position: sticky; top: 0; z-index: 50; border-bottom: 1px solid var(--h-line);
  background: rgba(247, 247, 251, .82);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
}
.hub-top__in { display: flex; align-items: center; justify-content: space-between; gap: var(--h-s2); padding: 16px 0; }
.hub .hub-logo { display: inline-flex; align-items: center; gap: 10px; font-size: 19px; font-weight: 700; letter-spacing: -.02em; color: var(--h-ink); text-decoration: none; }
.hub-logo__mark {
  width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center;
  background: linear-gradient(150deg, var(--h-violet), var(--h-violet-deep)); color: #fff;
}
.hub-logo__mark .material-symbols-outlined { font-size: 18px; }
.hub-top__right { display: flex; align-items: center; gap: var(--h-s2); }

.hub-chip {
  display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px; border-radius: 999px;
  border: 1px solid var(--h-line); background: var(--h-surface);
  font-family: var(--h-mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--h-ink-2);
}
/* Inside a glass header the chip goes glass too - an opaque white pill on a
   frosted bar reads as a sticker on the pane. */
.hub-glass .hub-chip {
  background: var(--h-glass-light-2); border-color: var(--h-glass-edge);
  -webkit-backdrop-filter: blur(var(--h-glass-blur-sm)); backdrop-filter: blur(var(--h-glass-blur-sm));
}
.hub-chip__dot { width: 6px; height: 6px; border-radius: 999px; background: var(--h-violet); box-shadow: 0 0 8px var(--h-violet); }

/* ---- buttons ------------------------------------------------------------ */
.hub-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 11px 20px; border-radius: var(--h-r); border: 1px solid transparent;
  font-family: inherit; font-size: 14.5px; font-weight: 600; text-decoration: none; cursor: pointer;
  transition: background-color .16s ease, border-color .16s ease;
}
.hub-btn--primary { background: var(--h-violet); color: #fff; }
.hub-btn--primary:hover { background: var(--h-violet-deep); }
.hub-btn--ghost { background: var(--h-surface); border-color: var(--h-line); color: var(--h-ink); }
.hub-btn--ghost:hover { border-color: var(--h-line-2); background: var(--h-surface-2); }
.hub-btn--lg { padding: 14px 26px; font-size: 15px; }
.hub-btn--block { width: 100%; }
.hub-btn[disabled] { opacity: .5; cursor: not-allowed; }
.hub-btn .material-symbols-outlined { font-size: 18px; }
.hub-textlink { font-size: 14.5px; font-weight: 500; color: var(--h-ink-2); text-decoration: none; }
.hub-textlink:hover { color: var(--h-ink); }

/* ---- type --------------------------------------------------------------- */
.hub-h1 { font-size: clamp(38px, 4.6vw, 56px); font-weight: 700; line-height: 1.06; letter-spacing: -.035em; margin: 0; }
.hub-h1 em { font-style: normal; color: var(--h-violet-2); }
.hub-h2 { font-size: clamp(26px, 2.8vw, 36px); font-weight: 700; line-height: 1.15; letter-spacing: -.03em; margin: 0; }
.hub-h3 { font-size: 18px; font-weight: 600; letter-spacing: -.01em; margin: 0; }
.hub-lede { font-size: 16.5px; line-height: 1.65; color: var(--h-ink-2); margin: 20px 0 0; }
.hub-body { font-size: 15.5px; line-height: 1.65; color: var(--h-ink-2); margin: 0; }
.hub-small { font-size: 13.5px; line-height: 1.6; color: var(--h-ink-2); margin: 0; }
.hub-mono { font-family: var(--h-mono); font-size: 11.5px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase; color: var(--h-violet-2); margin: 0; }
.hub-eyebrow { display: inline-flex; align-items: center; gap: 8px; padding: 7px 14px; border-radius: 999px; border: 1px solid var(--h-violet-line); background: var(--h-violet-soft); }

/* ---- surfaces ----------------------------------------------------------- */
.hub-card { background: var(--h-surface); border: 1px solid var(--h-line); border-radius: var(--h-r-lg); padding: var(--h-s3); }
.hub-card--flush { padding: 0; overflow: hidden; }
.hub-band { padding: var(--h-s5) 0; }
/* One dark band: it breaks a long light page and gives the steps somewhere to
   sit without competing with the hero. This is the inverse of what it used to
   be - the page was dark with a light band, and now it is the other way round. */
.hub-band--violet { background: var(--h-dk-bg); color: var(--h-dk-ink); }
.hub-band--violet .hub-h2, .hub-band--violet .hub-h3 { color: var(--h-dk-ink); }
.hub-band--violet .hub-body, .hub-band--violet .hub-small { color: var(--h-dk-ink-2); }
.hub-band--violet .hub-card { background: var(--h-dk-surface); border-color: var(--h-dk-line); }
.hub-band--violet .hub-mono, .hub-band--violet .hub-step__n { color: var(--h-dk-accent); }

/* ---- hero --------------------------------------------------------------- */
.hub-hero { position: relative; overflow: hidden; padding: var(--h-s5) 0 var(--h-s4); }
/* The single glow on the page, behind the headline. */
.hub-hero::before {
  content: ""; position: absolute; left: 50%; top: -240px; width: 900px; height: 640px;
  transform: translateX(-50%); pointer-events: none;
  background: radial-gradient(50% 50% at 50% 50%, rgba(124, 92, 255, .13), transparent 70%);
}
/* Centred hero. The lede and buttons centre with the headline: centring the
   title alone leaves the block hanging off its own text. */
.hub-hero__in { position: relative; z-index: 1; max-width: 760px; margin: 0 auto; text-align: center; }
.hub-hero__in .hub-lede { margin-left: auto; margin-right: auto; max-width: 62ch; }
.hub-hero__cta { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; margin-top: 34px; }

/* Three supporting points under the hero: icon, label, one line. */
.hub-points { position: relative; z-index: 1; display: grid; gap: var(--h-s3); grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); margin-top: var(--h-s5); }
.hub-point { display: flex; gap: 14px; }
.hub-point__ico { flex: 0 0 38px; height: 38px; border-radius: var(--h-r); display: grid; place-items: center; background: var(--h-violet-soft); border: 1px solid var(--h-violet-line); color: var(--h-violet-2); }
.hub-point__ico .material-symbols-outlined { font-size: 19px; }
.hub-point b { display: block; font-size: 14.5px; font-weight: 600; margin-bottom: 3px; }
.hub-point span { font-size: 13.5px; color: var(--h-ink-2); }

/* ---- report windows ----------------------------------------------------- */
.hub-shots { display: grid; gap: var(--h-s3); grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
.hub-mock { margin: 0; border-radius: var(--h-r-lg); border: 1px solid var(--h-line); background: var(--h-surface); overflow: hidden; }
.hub-mock__chrome { display: flex; align-items: center; gap: 6px; padding: 11px 16px; background: var(--h-surface-2); border-bottom: 1px solid var(--h-line); }
.hub-mock__chrome i { width: 9px; height: 9px; border-radius: 999px; background: rgba(0, 0, 0, .13); }
.hub-mock__chrome span { margin-left: 8px; font-family: var(--h-mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--h-ink-3); }
.hub-mock img { display: block; width: 100%; height: auto; }

/* ---- numbered steps ----------------------------------------------------- */
.hub-steps { display: grid; gap: var(--h-s3); grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); margin-top: var(--h-s4); }
.hub-step__top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; }
.hub-step__ico { width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center; background: var(--h-violet); color: #fff; }
.hub-step__n { font-family: var(--h-mono); font-size: 13px; font-weight: 600; color: var(--h-violet-deep); }

/* ---- feature row -------------------------------------------------------- */
.hub-feats { display: grid; gap: var(--h-s4); grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); margin-top: var(--h-s4); text-align: center; }
.hub-feat__ico { display: inline-grid; place-items: center; width: 44px; height: 44px; border-radius: 999px; background: var(--h-violet-soft); border: 1px solid var(--h-violet-line); color: var(--h-violet-2); margin-bottom: 14px; }
.hub-feat b { display: block; font-size: 15px; font-weight: 600; margin-bottom: 6px; }
.hub-feat span { font-size: 13.5px; color: var(--h-ink-2); }

/* ---- closing CTA -------------------------------------------------------- */
.hub-cta {
  position: relative; overflow: hidden; text-align: center;
  padding: var(--h-s5) var(--h-s3); border-radius: var(--h-r-lg);
  background: linear-gradient(150deg, #272247, #191630);
  border: 1px solid var(--h-violet-line); color: var(--h-dk-ink);
}
/* The one violet panel on a light page. Its ink is pinned because the tokens
   around it are now light. */
.hub-cta .hub-h2 { color: var(--h-dk-ink); }
.hub-cta .hub-body, .hub-cta .hub-small { color: var(--h-dk-ink-2); }
.hub-cta::before {
  content: ""; position: absolute; left: 50%; top: -180px; width: 700px; height: 420px;
  transform: translateX(-50%); pointer-events: none;
  background: radial-gradient(50% 50% at 50% 50%, rgba(124, 92, 255, .28), transparent 70%);
}
.hub-cta > * { position: relative; z-index: 1; }

/* ---- game cards (hub picker) -------------------------------------------- */
.hub-games { display: grid; gap: var(--h-s3); grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.hub-gcard {
  position: relative; display: flex; flex-direction: column; justify-content: flex-end;
  height: 340px; padding: 14px; border-radius: var(--h-r-lg); overflow: hidden;
  border: 1px solid var(--h-line); text-decoration: none; isolation: isolate;
  transition: border-color .18s ease;
}
.hub-gcard:hover { border-color: var(--h-violet-line); }
.hub-gcard__art { position: absolute; inset: 0; z-index: 0; background: linear-gradient(160deg, #29244A, #17142A); }
.hub-gcard__art--b { background: linear-gradient(160deg, #3A2244, #20132A); }
/* Key art sits inside the art slot, so if it fails to load the gradient behind
   it is still there. Slightly desaturated so the cards read as one set rather
   than as two competing box shots. */
.hub-gcard__img { width: 100%; height: 100%; object-fit: cover; display: block; filter: saturate(.9); transition: transform .35s ease; }
.hub-gcard:hover .hub-gcard__img { transform: scale(1.03); }
/* The scrim only supplies depth now - readability is the glass plate's job
   (below), so it no longer needs to crush the art to near-black. */
.hub-gcard__scrim { position: absolute; inset: 0; z-index: 1; background: linear-gradient(to bottom, transparent 45%, rgba(10, 8, 20, .40)); }
.hub-gcard__scrim--art { background: linear-gradient(to bottom, rgba(10, 8, 20, .08) 0%, rgba(10, 8, 20, .45) 100%); }
/* The card's text sits on a dark glass plate: blur guarantees the white text a
   readable ground on ANY art (the FC sheet is bright sky exactly where the
   title sits), where a translucent fill alone inherits whatever is behind it. */
.hub-gcard__in {
  position: relative; z-index: 2; padding: 14px; border-radius: 14px;
  background: var(--h-glass-dark);
  -webkit-backdrop-filter: blur(var(--h-glass-blur-md)) saturate(140%);
  backdrop-filter: blur(var(--h-glass-blur-md)) saturate(140%);
  border: 1px solid var(--h-glass-edge-dark);
}
.hub-gcard__tags { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
/* Tags are small glass chips. The dark fill + blur is what keeps the pair
   looking IDENTICAL on both cards - a white/10 fill with no blur reads white
   over the FC art's bright sky and near-black over the CS2 art. */
.hub-gtag {
  padding: 4px 9px; border-radius: 999px; font-family: var(--h-mono); font-size: 10.5px;
  letter-spacing: .08em; text-transform: uppercase; color: rgba(255, 255, 255, .9);
  background: rgba(10, 8, 20, .35);
  -webkit-backdrop-filter: blur(var(--h-glass-blur-sm)); backdrop-filter: blur(var(--h-glass-blur-sm));
  border: 1px solid var(--h-glass-edge-dark);
}
.hub-gtag--active { background: rgba(124, 92, 255, .60); border-color: rgba(255, 255, 255, .28); color: #fff; }
.hub-gcard__name { font-size: 24px; font-weight: 700; letter-spacing: -.025em; margin: 0 0 4px; color: #fff; }
.hub-gcard__desc { font-size: 13.5px; color: rgba(255, 255, 255, .78); margin: 0; }
.hub-gsoon {
  display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center;
  height: 340px; padding: 24px; border-radius: var(--h-r-lg);
  background: rgba(255, 255, 255, .35); border: 1px dashed var(--h-line-2);
  -webkit-backdrop-filter: blur(var(--h-glass-blur-md)) saturate(140%);
  backdrop-filter: blur(var(--h-glass-blur-md)) saturate(140%);
}
.hub-gsoon__ring { width: 46px; height: 46px; border-radius: 999px; background: rgba(255, 255, 255, .6); border: 1px solid var(--h-glass-edge); display: grid; place-items: center; margin-bottom: 16px; color: var(--h-ink-3); }
.hub-game__mark { width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center; background: var(--h-violet-soft); border: 1px solid var(--h-violet-line); color: var(--h-violet-2); }

/* ---- game hub picker: featured hero + "your games" list ----------------- */
.hub-picker { display: grid; gap: var(--h-s4); grid-template-columns: 1.7fr 1fr; align-items: start; }
@media (max-width: 860px) { .hub-picker { grid-template-columns: 1fr; } }

.hub-picker__hero { position: relative; }
/* The one glow on this view, behind the featured game - same restraint rule
   as the landing hero: at most once per view, behind the thing being sold. */
.hub-picker__hero::before {
  content: ""; position: absolute; left: 50%; top: -110px; width: 620px; height: 460px;
  transform: translateX(-50%); pointer-events: none; z-index: 0;
  background: radial-gradient(50% 50% at 50% 50%, rgba(124, 92, 255, .16), transparent 70%);
}

/* Carousel: every slide stacks in the same box, only the active one shows. A
   real carousel over real games (dot count = games you actually have),
   not a decorative "1/5" over content that never changes. */
.hub-hero__slides { position: relative; z-index: 1; height: 460px; }
.hub-hero-slide {
  position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: flex-end;
  padding: 36px; border-radius: var(--h-r-lg); overflow: hidden;
  border: 1px solid var(--h-line); text-decoration: none; isolation: isolate;
  opacity: 0; visibility: hidden; transition: opacity .25s ease, border-color .18s ease;
}
.hub-hero-slide.is-active { opacity: 1; visibility: visible; }
.hub-hero-slide:hover { border-color: var(--h-violet-line); }
.hub-hero-slide__art { position: absolute; inset: 0; z-index: 0; background: linear-gradient(160deg, #29244A, #17142A); }
.hub-hero-slide__art--b { background: linear-gradient(160deg, #3A2244, #20132A); }
.hub-hero-slide__img { width: 100%; height: 100%; object-fit: cover; display: block; filter: saturate(.9); transition: transform .35s ease; }
.hub-hero-slide:hover .hub-hero-slide__img { transform: scale(1.02); }
.hub-hero-slide__scrim { position: absolute; inset: 0; z-index: 1; background: linear-gradient(to bottom, rgba(10, 8, 20, .1) 0%, rgba(10, 8, 20, .5) 45%, rgba(10, 8, 20, .86) 100%); }
.hub-hero-slide__in { position: relative; z-index: 2; max-width: 460px; }
.hub-hero-slide__name { font-size: 34px; font-weight: 700; letter-spacing: -.025em; margin: 14px 0 8px; color: #fff; }
.hub-hero-slide__desc { font-size: 14.5px; color: rgba(255, 255, 255, .75); margin: 0 0 22px; }
.hub-hero-slide__cta { display: inline-flex; }
.hub-hero-slide__cta .material-symbols-outlined { font-size: 18px; }

/* Carousel controls sit top-right of the hero, clear of the CTA/copy which
   is bottom-left - arrows + dots, wired to real slides. */
.hub-hero__nav { position: absolute; z-index: 3; right: 24px; top: 24px; display: flex; align-items: center; gap: 14px; }
.hub-hero__arrow {
  width: 34px; height: 34px; border-radius: 999px; display: grid; place-items: center;
  background: rgba(255, 255, 255, .14); border: 1px solid rgba(255, 255, 255, .22); color: #fff;
  cursor: pointer; transition: background-color .15s ease;
}
.hub-hero__arrow:hover { background: rgba(255, 255, 255, .24); }
.hub-hero__arrow .material-symbols-outlined { font-size: 17px; }
.hub-hero__dots { display: flex; gap: 6px; }
.hub-hero__dot { width: 6px; height: 6px; border-radius: 999px; background: rgba(255, 255, 255, .35); border: 0; cursor: pointer; padding: 0; transition: background-color .15s ease, width .15s ease; }
.hub-hero__dot.is-active { background: #fff; width: 18px; }

/* "Your games" panel - a bordered card (not loose rows) with a divided list,
   closer to a real library panel than a stack of separate boxes. */
.hub-lib {
  border-radius: var(--h-r-lg); padding: var(--h-s3);
  background: rgba(255, 255, 255, .55); border: 1px solid rgba(255, 255, 255, .6);
  -webkit-backdrop-filter: blur(20px) saturate(160%); backdrop-filter: blur(20px) saturate(160%);
  box-shadow: 0 8px 30px rgba(20, 18, 31, .06);
}
.hub-lib__title { font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--h-ink-3); margin-bottom: 14px; }
.hub-lib__list { display: flex; flex-direction: column; }
.hub-lib__item {
  display: flex; align-items: center; gap: 12px; padding: 10px 6px; text-decoration: none; color: var(--h-ink);
  border-bottom: 1px solid var(--h-line); transition: background-color .15s ease;
}
.hub-lib__list .hub-lib__item:last-child { border-bottom: 0; }
.hub-lib__item:hover { background: var(--h-violet-soft); border-radius: var(--h-r); }
.hub-lib__item .material-symbols-outlined { color: var(--h-ink-3); font-size: 20px; }
.hub-lib__thumb { flex: 0 0 52px; width: 52px; height: 52px; border-radius: 8px; overflow: hidden; background: linear-gradient(160deg, #29244A, #17142A); }
.hub-lib__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; filter: saturate(.9); }
.hub-lib__meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.hub-lib__name { font-size: 14.5px; font-weight: 600; }
.hub-lib__genre { font-family: var(--h-mono); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--h-ink-3); }

/* "More titles" - full-width row below, mirrors "you might also like" but
   with only what's real: a single placeholder, not invented game covers. */
.hub-more { margin-top: var(--h-s5); }
.hub-more__row { display: grid; gap: var(--h-s3); grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.hub-lib__soon { display: flex; align-items: center; gap: 14px; padding: 20px; border-radius: var(--h-r-lg); border: 1px dashed var(--h-line-2); }
.hub-lib__soon .material-symbols-outlined { flex: 0 0 auto; color: var(--h-ink-3); font-size: 26px; }
.hub-lib__soon-t { display: block; font-size: 14.5px; font-weight: 600; }
.hub-lib__soon-s { display: block; font-size: 13px; color: var(--h-ink-3); margin-top: 3px; }

/* Search box in the top bar, functional filter over "Your games". */
.hub-search { position: relative; flex: 1; max-width: 380px; }
.hub-search input {
  width: 100%; padding: 9px 14px 9px 38px; border-radius: 999px; border: 1px solid var(--h-glass-edge);
  background: var(--h-glass-light-2); color: var(--h-ink); font: inherit; font-size: 13.5px;
  -webkit-backdrop-filter: blur(var(--h-glass-blur-sm)); backdrop-filter: blur(var(--h-glass-blur-sm));
}
/* Focus is a SOLID border, not a translucent one - it has to stay visible over
   whatever the glass lets through. */
.hub-search input:focus { outline: none; border-color: var(--h-violet); background: rgba(255, 255, 255, .8); }
.hub-search .material-symbols-outlined { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--h-ink-3); font-size: 18px; pointer-events: none; }

/* ---- forms -------------------------------------------------------------- */
.hub-center { min-height: 100vh; display: grid; place-items: center; padding: var(--h-s3); }
.hub-auth { width: 100%; max-width: 400px; }
.hub-field { display: block; margin-bottom: var(--h-s2); }
.hub-label { display: flex; justify-content: space-between; font-family: var(--h-mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--h-ink-2); margin-bottom: 8px; }
.hub-input {
  width: 100%; padding: 12px 14px; border-radius: var(--h-r);
  border: 1px solid var(--h-line-2); background: var(--h-bg-2);
  font-family: inherit; font-size: 15px; color: var(--h-ink);
  transition: border-color .16s ease, box-shadow .16s ease;
}
.hub-input::placeholder { color: var(--h-ink-3); }
.hub-input:focus { outline: none; border-color: var(--h-violet); box-shadow: 0 0 0 3px rgba(124, 92, 255, .16); }
.hub-err { display: block; margin-top: 8px; font-size: 13px; color: var(--h-bad); }

/* ---- footer ------------------------------------------------------------- */
.hub-foot { border-top: 1px solid var(--h-line); background: var(--h-bg-2); }
.hub-foot__in { display: flex; align-items: center; justify-content: space-between; gap: var(--h-s3); flex-wrap: wrap; padding: var(--h-s4) 0; }
.hub-foot__nav { display: flex; gap: var(--h-s3); flex-wrap: wrap; }
.hub-foot__nav a { font-size: 13.5px; color: var(--h-ink-2); text-decoration: none; }
.hub-foot__nav a:hover { color: var(--h-ink); }
.hub-foot__copy { font-size: 13px; color: var(--h-ink-3); }
.hub-foot__copy strong { color: var(--h-ink-2); font-weight: 600; }
/* Signed-in (hub) variant: a full-width band BELOW the shell, running under
   the sidebar and the content alike. Glass like the panels above it, but
   squared off and bordered only on top - a footer is a band, not a card. */
/* Doubled class: .hub-glass is defined later in the file, and its border /
   box-shadow shorthands would win on equal specificity. */
.hub-foot--hub.hub-glass, .hub-foot--hub { border-width: 1px 0 0; border-radius: 0; box-shadow: none; }
.hub-foot__grid {
  display: grid; grid-template-columns: 2.2fr 1fr 1fr; gap: var(--h-s4);
  padding: var(--h-s4) 0 var(--h-s3);
}
@media (max-width: 720px) { .hub-foot__grid { grid-template-columns: 1fr; gap: var(--h-s3); } }
.hub-foot__tag { max-width: 34ch; font-size: 13.5px; line-height: 1.6; color: var(--h-ink-2); margin: 12px 0 0; }
.hub-foot__h {
  font-family: var(--h-mono); font-size: 11px; font-weight: 500; letter-spacing: .1em;
  text-transform: uppercase; color: var(--h-ink-3); margin: 4px 0 12px;
}
.hub-foot__links { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.hub-foot__links a { font-size: 13.5px; color: var(--h-ink-2); text-decoration: none; }
.hub-foot__links a:hover { color: var(--h-ink); }
.hub-foot__bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--h-s2);
  flex-wrap: wrap; padding: 18px 0 22px; border-top: 1px solid var(--h-line);
  font-size: 13px; color: var(--h-ink-3);
}
.hub-foot__bar strong { color: var(--h-ink-2); font-weight: 600; }

/* ---- signed-in page layout --------------------------------------------- */
/* One row of cards does not fill a screen, so the document used to end partway
   up the viewport and leave bare background below the footer. Pin the footer to
   the bottom and let main absorb the slack instead. */
body[data-hub-page="hub"] { min-height: 100dvh; }
.hub-content { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
/* Main absorbs the slack AND centres its content in it - inert once the
   dashboard has enough content to fill the height (flex slack is simply zero
   then), but a sparse first-day page still sits composed, not top-hugging. */
.hub-content > main {
  flex: 1 0 auto; width: 100%;
  display: flex; flex-direction: column; justify-content: center; gap: 28px;
}

/* ---- signed-in dashboard ------------------------------------------------- */
.hub-dsec__h { display: flex; align-items: baseline; gap: 12px; margin-bottom: 14px; }
.hub-dsec__h h2 { font-size: 20px; font-weight: 700; letter-spacing: -.02em; margin: 0; }

/* Hero: the newest report, any game. Same anatomy as a game card - art, soft
   scrim, dark glass plate - just bigger and with the report's own words. */
.hub-dhero {
  position: relative; display: flex; flex-direction: column; justify-content: flex-end;
  min-height: 380px; padding: 14px; border-radius: var(--h-r-lg); overflow: hidden;
  border: 1px solid var(--h-line); isolation: isolate;
}
.hub-dhero__art { position: absolute; inset: 0; z-index: 0; background: linear-gradient(160deg, #29244A, #17142A); }
.hub-dhero__art img { width: 100%; height: 100%; object-fit: cover; display: block; filter: saturate(.9); }
.hub-dhero__scrim { position: absolute; inset: 0; z-index: 1; background: linear-gradient(to bottom, rgba(10, 8, 20, .05) 40%, rgba(10, 8, 20, .45)); }
.hub-dhero__plate {
  position: relative; z-index: 2; padding: 18px; border-radius: 14px; max-width: 640px;
  background: var(--h-glass-dark);
  -webkit-backdrop-filter: blur(var(--h-glass-blur-md)) saturate(140%);
  backdrop-filter: blur(var(--h-glass-blur-md)) saturate(140%);
  border: 1px solid var(--h-glass-edge-dark);
}
.hub-dhero__name { font-size: 26px; font-weight: 700; letter-spacing: -.02em; color: #fff; margin: 0 0 6px; }
.hub-dhero__when { font-size: 15px; font-weight: 500; color: rgba(255, 255, 255, .65); letter-spacing: 0; }
.hub-dhero__ins { font-size: 14px; line-height: 1.55; color: rgba(255, 255, 255, .82); margin: 0 0 14px; max-width: 56ch; }
.hub-dhero__cta { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
/* First-run variant: no art to sit on, so the panel is light glass with its
   own violet glow, and everything centres - a left-aligned paragraph floating
   in a huge white box reads as a rendering accident, not a welcome. */
.hub-dhero--empty { min-height: 280px; align-items: center; justify-content: center; text-align: center; padding: 40px 24px; }
.hub-dhero--empty::before {
  content: ""; position: absolute; left: 50%; top: -160px; width: 640px; height: 400px;
  transform: translateX(-50%); pointer-events: none; z-index: 0;
  background: radial-gradient(50% 50% at 50% 50%, rgba(124, 92, 255, .18), transparent 70%);
}
.hub-dhero--empty .hub-dhero__plate {
  display: flex; flex-direction: column; align-items: center;
  background: none; border: 0; padding: 0; max-width: 560px;
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
.hub-dhero__ring {
  width: 52px; height: 52px; border-radius: 999px; display: grid; place-items: center;
  margin-bottom: 16px; background: var(--h-violet-soft); border: 1px solid var(--h-violet-line);
  color: var(--h-violet-2);
}
.hub-dhero__ring .material-symbols-outlined { font-size: 26px; }
.hub-dhero--empty .hub-dhero__name { color: var(--h-ink); font-size: clamp(22px, 2.4vw, 30px); }
.hub-dhero--empty .hub-dhero__ins { color: var(--h-ink-2); margin-bottom: 18px; }
.hub-dhero--empty .hub-dhero__cta { justify-content: center; }

/* Inside the dashboard the main column is ~880px, not the landing's 1180: at
   280px minimum the game grid snaps to two oversized cards and orphans the
   "coming soon" card onto its own row. Tighter minimum -> three balanced
   columns; the cards shrink with it. */
.hub-dsec .hub-games { grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 16px; }
.hub-dsec .hub-gcard, .hub-dsec .hub-gsoon { height: 300px; }
.hub-dsec .hub-gcard__name { font-size: 20px; }
.hub-dsec .hub-gcard__desc { font-size: 12.5px; }

/* Report cards: mixed-game by design - the game is the badge, not the section. */
.hub-dcards { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); }
.hub-dcard {
  display: flex; flex-direction: column; border-radius: var(--h-r-lg); overflow: hidden;
  border: 1px solid var(--h-line); text-decoration: none; color: var(--h-ink);
  background: var(--h-glass-light-2);
  -webkit-backdrop-filter: blur(var(--h-glass-blur-md)); backdrop-filter: blur(var(--h-glass-blur-md));
  transition: border-color .18s ease;
}
.hub-dcard:hover { border-color: var(--h-violet-line); }
.hub-dcard__art { position: relative; height: 120px; overflow: hidden; }
.hub-dcard__artin { position: absolute; inset: 0; display: block; background: linear-gradient(160deg, #29244A, #17142A); }
.hub-dcard__artin img { width: 100%; height: 100%; object-fit: cover; display: block; filter: saturate(.9); }
.hub-dcard__score {
  position: absolute; right: 10px; bottom: 10px; padding: 4px 10px; border-radius: 999px;
  font-family: var(--h-mono); font-size: 12px; font-weight: 600; color: #fff;
  background: rgba(10, 8, 20, .5); border: 1px solid var(--h-glass-edge-dark);
  -webkit-backdrop-filter: blur(var(--h-glass-blur-sm)); backdrop-filter: blur(var(--h-glass-blur-sm));
}
.hub-dcard__in { display: flex; flex-direction: column; gap: 6px; padding: 14px; }
.hub-dcard__meta { display: flex; align-items: center; gap: 8px; }
.hub-dcard__meta time { font-size: 11.5px; color: var(--h-ink-3); }
.hub-dcard b { font-size: 14.5px; }
.hub-dcard p { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--h-ink-2); }
.hub-gbadge {
  font-family: var(--h-mono); font-size: 9.5px; letter-spacing: .07em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 999px;
  background: var(--h-violet-soft); border: 1px solid var(--h-violet-line); color: var(--h-violet-2);
}

/* Right rail panels */
.hub-rp { border-radius: var(--h-r-lg); padding: 16px; }
.hub-rp__t { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--h-ink-3); margin: 0 0 12px; }
.hub-rp__feed { display: flex; flex-direction: column; gap: 12px; }
.hub-rp__i { display: flex; gap: 10px; align-items: flex-start; }
.hub-rp__i .material-symbols-outlined { font-size: 18px; color: var(--h-violet-2); margin-top: 1px; }
.hub-rp__i b { display: block; font-size: 13px; font-weight: 600; }
.hub-rp__i > span > span { font-size: 11.5px; color: var(--h-ink-3); }

/* Sidebar identity chip */
.hub-me { display: flex; align-items: center; gap: 10px; padding: 0 8px 14px; }
.hub-me__av {
  width: 34px; height: 34px; flex: 0 0 34px; border-radius: 999px; display: grid; place-items: center;
  background: linear-gradient(150deg, var(--h-violet), var(--h-violet-deep)); color: #fff;
  font-size: 12px; font-weight: 700;
}
.hub-me__id { min-width: 0; }
.hub-me__id b { display: block; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hub-me__id > span { font-size: 11px; color: var(--h-ink-3); }

/* ---- glass variant: sidebar + frosted panels ---------------------------- */
/* A blurred colour field behind everything is what glassmorphism needs to
   show through - without it "backdrop-filter: blur" has nothing to blur. */
.hub--glass {
  position: relative; overflow-x: hidden;
  /* A tinted field, not flat white: without colour under them the frosted
     panels read as plain boxes and the whole page goes paper-flat. */
  background:
    radial-gradient(900px 600px at 85% 8%, rgba(124, 92, 255, .12), transparent 70%),
    radial-gradient(700px 700px at 12% 90%, rgba(124, 92, 255, .10), transparent 70%),
    linear-gradient(160deg, #F3F1FE 0%, #F7F7FB 45%, #EFECFC 100%);
}
.hub--glass::before, .hub--glass::after {
  content: ""; position: fixed; z-index: -1; border-radius: 50%; filter: blur(90px); pointer-events: none;
}
.hub--glass::before { width: 680px; height: 680px; top: -220px; left: -160px; background: rgba(124, 92, 255, .38); }
.hub--glass::after { width: 560px; height: 560px; bottom: -220px; right: -140px; background: rgba(124, 92, 255, .30); }

.hub-glass {
  background: var(--h-glass-light);
  -webkit-backdrop-filter: blur(var(--h-glass-blur-lg)) saturate(160%);
  backdrop-filter: blur(var(--h-glass-blur-lg)) saturate(160%);
  border: 1px solid var(--h-glass-edge);
  box-shadow: 0 8px 30px rgba(20, 18, 31, .06);
}

/* Glass is a preference, not a requirement: anyone who asked their OS for less
   transparency gets solid surfaces with the same layout, and text plates go
   opaque so contrast stops depending on what is underneath. */
@media (prefers-reduced-transparency: reduce) {
  .hub-glass, .hub-search input, .hub-glass .hub-chip, .hub-gsoon, .hub-dcard {
    background: var(--h-surface); -webkit-backdrop-filter: none; backdrop-filter: none;
    border-color: var(--h-line);
  }
  .hub-gcard__in, .hub-gtag, .hub-dhero__plate, .hub-dcard__score {
    background: rgba(10, 8, 20, .85); -webkit-backdrop-filter: none; backdrop-filter: none;
  }
  .hub--glass::before, .hub--glass::after { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .hub-gcard__img, .hub-hero-slide__img { transition: none; }
  .hub-gcard:hover .hub-gcard__img, .hub-hero-slide:hover .hub-hero-slide__img { transform: none; }
}

/* Dashboard shell: sidebar | main | rail as floating glass panels in a padded,
   centred field - not full-bleed columns. Rails go sticky; main scrolls. */
.hub-shell {
  display: flex; gap: 20px; align-items: flex-start;
  max-width: 1460px; margin: 0 auto; padding: 20px 24px 48px; min-height: 100dvh;
}

.hub-side {
  flex: 0 0 216px; position: sticky; top: 20px;
  /* Full column height, not content height - the foot (user info + sign out)
     pins to the BOTTOM of the viewport, like a real app rail. */
  height: calc(100dvh - 40px); max-height: calc(100dvh - 40px); overflow-y: auto;
  display: flex; flex-direction: column; gap: 28px; padding: 18px 12px;
}
.hub-rail {
  flex: 0 0 280px; position: sticky; top: 20px;
  display: flex; flex-direction: column; gap: 16px;
}
@media (max-width: 1100px) { .hub-rail { display: none; } }

/* Below 860px the sidebar (the only nav chrome on these pages - Home, Upload,
   Reports, Moments, Statistics, My locker/office, Account, Sign out) used to
   just disappear with nothing replacing it, leaving no way to navigate except
   the browser back button. It's now an off-canvas drawer instead: same markup,
   toggled by a hamburger button that only exists at this width. */
/* The doubled class is deliberate - do not "simplify" it back to a single one.
   The button is markup'd as `class="hub-theme hub-mobilenav-btn"` (app-shell.js
   and home/index.html), and .hub-theme (hub-dark.css) sets `display: grid` for
   the sidebar's theme/palette buttons. EVERY app page loads hub-dark.css AFTER
   hub.css, so at equal specificity (0,1,0) the later sheet won and a bare
   `.hub-mobilenav-btn { display: none }` never actually applied: the hamburger
   showed at every width, including desktop where the sidebar is already on
   screen next to it. Matching both classes (0,2,0) wins on specificity rather
   than source order, so no future stylesheet reshuffle can silently undo it. */
.hub-theme.hub-mobilenav-btn { display: none; }
.hub-scrim { display: none; }
@media (max-width: 860px) {
  .hub-theme.hub-mobilenav-btn {
    display: grid; position: fixed; top: 14px; left: 14px; z-index: 61;
  }
  /* Room for the fixed button above, or it sits on top of the page title. */
  .hub-shell { padding-top: 64px; }
  .hub-side {
    position: fixed; inset: 0 auto 0 0; z-index: 60;
    width: min(260px, 82vw); height: 100dvh; max-height: 100dvh;
    border-radius: 0; box-shadow: 0 0 40px rgba(0, 0, 0, .35);
    transform: translateX(-100%);
    /* visibility, not transform alone. A translated box is off SCREEN but still
       in the tab order and the accessibility tree, so a keyboard user on a phone
       tabbed through all ten closed-drawer links - Home, Upload, Reports,
       Moments, Statistics, locker, Account, Sign in, theme, palette - with focus
       apparently nowhere, and screen readers read the lot. visibility:hidden
       removes it from both.
       It is in the transition on purpose: visibility interpolates discretely but
       is special-cased so `visible` holds for the whole duration when it is
       either endpoint. So opening shows it instantly and closing keeps it
       painted until the slide finishes - the animation is unchanged. */
    visibility: hidden;
    transition: transform .22s ease, visibility .22s ease;
  }
  .hub-side.is-open { transform: translateX(0); visibility: visible; }
  .hub-scrim {
    display: block; position: fixed; inset: 0; z-index: 59;
    background: rgba(0, 0, 0, .4); opacity: 0; pointer-events: none;
    transition: opacity .22s ease;
  }
  .hub-scrim.is-open { opacity: 1; pointer-events: auto; }
}
.hub-side__nav { display: flex; flex-direction: column; gap: 4px; flex: 1; }
.hub-side__link {
  display: flex; align-items: center; gap: 12px; padding: 10px 14px; border-radius: var(--h-r);
  color: var(--h-ink-2); text-decoration: none; font-size: 14px; font-weight: 500;
  transition: background-color .15s ease, color .15s ease;
}
.hub-side__link .material-symbols-outlined { font-size: 20px; }
.hub-side__link:hover { background: rgba(124, 92, 255, .08); color: var(--h-ink); }
.hub-side__link.is-active { background: var(--h-violet); color: #fff; }
.hub-side__foot { border-top: 1px solid var(--h-line); padding-top: 16px; }


/* ---- OAuth buttons ------------------------------------------------------ */
/* Neutral rather than each provider's brand colour: two saturated buttons
   stacked would fight each other and the primary action below. The logo carries
   the recognition; the button just has to be obviously clickable. */
.hub-oauth { display: grid; gap: 10px; margin-bottom: 18px; }
.hub-oauth__btn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; padding: 12px 16px; border-radius: var(--h-r);
  border: 1px solid var(--h-line-2); background: var(--h-bg-2);
  font-family: inherit; font-size: 14.5px; font-weight: 600; color: var(--h-ink);
  cursor: pointer; transition: border-color .16s ease, background-color .16s ease;
}
.hub-oauth__btn:hover { border-color: var(--h-ink-3); background: var(--h-surface-2); }
.hub-oauth__ico { width: 18px; height: 18px; flex: 0 0 18px; }
.hub-oauth__note {
  margin: -8px 0 14px; font-size: 13px; line-height: 1.5;
  color: var(--h-ink-2); text-align: center;
}

/* "or" rule between the OAuth block and the email form. */
.hub-or {
  display: flex; align-items: center; gap: 12px;
  margin: 0 0 18px; color: var(--h-ink-3); font-size: 12px;
}
.hub-or::before, .hub-or::after {
  content: ""; flex: 1; height: 1px; background: var(--h-line);
}
