/* ============================================================================================
   THEME TOKENS — the only place colours are defined.
   ============================================================================================

   Everything is derived from a handful of source values. Change --brand-h (the hue) and every
   button, border and highlight in the whole site moves with it, because nothing below ever writes
   a literal colour — they are all hsl() built from these numbers.

   Want a different accent?              change --brand-h
   Want the whole UI warmer or cooler?   change --surface-h
   Want more or less colour?             change --brand-s / --surface-s

   RGB / rainbow mode is already possible: --brand-h is a plain number, so animating it cycles the
   entire interface. See the .rainbow class at the bottom.

   The look is deliberately PLAIN: flat backgrounds, solid buttons, one accent colour. Glows,
   gradient text and gradient buttons were tried and removed — they made a small game's site read
   like a template.
   ============================================================================================ */

:root {
  /* ---- the source of truth: one hue, one surface tint ------------------------------------- */
  --brand-h: 330;         /* pink. 270 = purple, 200 = blue, 150 = green … */
  --brand-s: 80%;

  --surface-h: 265;       /* tint of the greys/whites — keeps the UI from looking dead */
  --surface-s: 14%;

  /* ---- derived brand ramp ---------------------------------------------------------------- */
  --brand:        hsl(var(--brand-h) var(--brand-s) 62%);
  --brand-strong: hsl(var(--brand-h) var(--brand-s) 54%);

  /* ---- radii, motion --------------------------------------------------------------------- */
  --r-sm: 8px;
  --r-md: 10px;
  --ring: 0 0 0 3px hsl(var(--brand-h) var(--brand-s) 62% / .3);
  --ease: cubic-bezier(.4, 0, .2, 1);
}

/* ---- DARK (default) ---------------------------------------------------------------------- */
:root,
:root[data-theme="dark"] {
  color-scheme: dark;
  --scrollbar-thumb: hsl(var(--surface-h) 10% 45%);
  scrollbar-color: var(--scrollbar-thumb) var(--surface);
  --bg:        hsl(var(--surface-h) calc(var(--surface-s) * .7) 8%);
  --surface:   hsl(var(--surface-h) var(--surface-s) 12%);
  --surface-2: hsl(var(--surface-h) var(--surface-s) 17%);
  --line:      hsl(var(--surface-h) var(--surface-s) 100% / .12);
  --line-soft: hsl(var(--surface-h) var(--surface-s) 100% / .07);

  --text:      hsl(var(--surface-h) 16% 95%);
  --text-dim:  hsl(var(--surface-h) 12% 95% / .6);
  --text-faint:hsl(var(--surface-h) 12% 95% / .36);

  --on-brand:  hsl(0 0% 100%);   /* text ON a brand-coloured button: white, like the landing doors (Rainy, 2026-09-30) */
  --danger:    hsl(5 80% 68%);
  --ok:        hsl(145 50% 62%);
}

/* ---- LIGHT — soft pinks ------------------------------------------------------------------ */
:root[data-theme="light"] {
  color-scheme: light;
  --scrollbar-thumb: hsl(var(--surface-h) 10% 55%);
  --bg:        hsl(var(--brand-h) 100% 98%);
  --surface:   hsl(0 0% 100%);
  --surface-2: hsl(var(--brand-h) 70% 97%);
  --line:      hsl(var(--brand-h) 40% 62% / .26);
  --line-soft: hsl(var(--brand-h) 40% 62% / .14);

  --text:      hsl(var(--brand-h) 30% 20%);
  --text-dim:  hsl(var(--brand-h) 20% 32% / .75);
  --text-faint:hsl(var(--brand-h) 18% 38% / .5);

  --on-brand:  hsl(0 0% 100%);
  --danger:    hsl(5 70% 46%);
  --ok:        hsl(145 48% 32%);

  --brand:        hsl(var(--brand-h) 72% 58%);
  --brand-strong: hsl(var(--brand-h) 68% 48%);
}

/* Follow the operating system until the player chooses for themselves. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    --bg: hsl(var(--brand-h) 100% 98%);
    --text: hsl(var(--brand-h) 30% 20%);
    --sky: var(--sky-clouds);
    --sky-size: var(--sky-clouds-size);
  }
}

/* ============================================================================================
   THE SKY — stars at night, clouds in the day.
   ============================================================================================

   Drawn with gradients rather than an image: nothing to download, it scales to any screen, and it
   costs one paint. No animation, deliberately — a moving background on a page people read is a
   distraction, and a drifting layer is the first thing to stutter on a cheap phone.

   Kept faint on purpose. This is texture, not decoration: the site is meant to look plain, and the
   moment you can pick out individual clouds it starts looking like a template again. Tiled at two
   different sizes so the repeat is hard to spot.
   ============================================================================================ */

:root {
  --sky-stars:
    radial-gradient(1.6px 1.6px at  18%  22%, hsl(0 0% 100% / .80), transparent 64%),
    radial-gradient(1.1px 1.1px at  62%  11%, hsl(0 0% 100% / .55), transparent 64%),
    radial-gradient(1.3px 1.3px at  84%  36%, hsl(0 0% 100% / .70), transparent 64%),
    radial-gradient(  1px   1px at  35%  58%, hsl(0 0% 100% / .45), transparent 64%),
    radial-gradient(1.5px 1.5px at   8%  74%, hsl(0 0% 100% / .65), transparent 64%),
    radial-gradient(1.1px 1.1px at  71%  81%, hsl(0 0% 100% / .50), transparent 64%),
    radial-gradient(1.2px 1.2px at  46%  93%, hsl(0 0% 100% / .60), transparent 64%),
    radial-gradient(  1px   1px at  93%  67%, hsl(0 0% 100% / .40), transparent 64%),
    /* Second, denser tile — different size, so the two never line up into an obvious grid. */
    radial-gradient(  1px   1px at  27%  14%, hsl(0 0% 100% / .40), transparent 64%),
    radial-gradient(1.2px 1.2px at  77%  48%, hsl(0 0% 100% / .35), transparent 64%),
    radial-gradient(  1px   1px at  52%  72%, hsl(0 0% 100% / .45), transparent 64%),
    radial-gradient(  1px   1px at  13%  41%, hsl(0 0% 100% / .30), transparent 64%);

  --sky-stars-size:
    420px 420px, 420px 420px, 420px 420px, 420px 420px,
    420px 420px, 420px 420px, 420px 420px, 420px 420px,
    260px 260px, 260px 260px, 260px 260px, 260px 260px;

  /* Soft and wide. Clouds read as clouds because they are flat-bottomed and much wider than they
     are tall — a round blob just looks like a smudge. */
  --sky-clouds:
    radial-gradient(120px 44px at 16% 20%, hsl(0 0% 100% / .95), transparent 72%),
    radial-gradient( 78px 32px at 27% 24%, hsl(0 0% 100% / .85), transparent 72%),
    radial-gradient(150px 52px at 68% 52%, hsl(0 0% 100% / .90), transparent 72%),
    radial-gradient( 92px 36px at 56% 56%, hsl(0 0% 100% / .80), transparent 72%),
    radial-gradient(110px 40px at 38% 84%, hsl(0 0% 100% / .85), transparent 72%),
    radial-gradient( 84px 30px at 88% 12%, hsl(0 0% 100% / .75), transparent 72%);

  --sky-clouds-size:
    820px 560px, 820px 560px, 820px 560px, 820px 560px, 820px 560px, 820px 560px;

  /* Dark is the default, so stars are the default. */
  --sky: var(--sky-stars);
  --sky-size: var(--sky-stars-size);
}

:root[data-theme="dark"] {
  --sky: var(--sky-stars);
  --sky-size: var(--sky-stars-size);
}

:root[data-theme="light"] {
  --sky: var(--sky-clouds);
  --sky-size: var(--sky-clouds-size);
}

/* ============================================================================================
   BASE
   ============================================================================================ */

* { box-sizing: border-box; }

/* Any class that sets `display` (.btn, .row …) would otherwise beat the UA stylesheet's
   [hidden] { display: none }, and elements meant to be hidden would render anyway. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  /* Colour and sky set separately: `background:` shorthand would wipe out --sky. */
  background-color: var(--bg);
  background-image: var(--sky);
  background-size: var(--sky-size);
  background-repeat: repeat;
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3 { margin: 0; line-height: 1.25; font-weight: 650; }
h1 { font-size: clamp(1.7rem, 4vw, 2.2rem); }
h2 { font-size: 1.1rem; }

.muted { color: var(--text-dim); }
.faint { color: var(--text-faint); font-size: .82rem; }

/* The game's name. One colour, no gradient. */
.brand-text { color: var(--brand); }

/* ============================================================================================
   LAYOUT
   ============================================================================================ */

.wrap { width: min(920px, 92vw); margin: 0 auto; }
.center { min-height: 100%; display: grid; place-items: center; padding: 32px 0; }

.card {
  padding: clamp(20px, 4vw, 28px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}

.row { display: flex; gap: 8px; flex-wrap: wrap; }
.row.tight { gap: 6px; }
.stack > * + * { margin-top: 14px; }
.spread { display: flex; align-items: center; justify-content: space-between; gap: 14px; }

/* ============================================================================================
   CONTROLS — every one built from the tokens above
   ============================================================================================ */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 10px 16px;
  font: inherit; font-weight: 550;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: border-color .15s var(--ease), background .15s var(--ease), color .15s var(--ease);
}
.btn:hover { border-color: var(--brand); }
/* Discord's own colour, so "Connect Discord" is recognised rather than read. */
.btn.discord-btn { color: #fff; background: #5865F2; border-color: #5865F2; }
.btn.discord-btn:hover { background: #4752C4; border-color: #4752C4; }
.btn:focus-visible { outline: none; box-shadow: var(--ring); }
.btn:disabled { opacity: .5; cursor: default; }

.btn.primary {
  color: var(--on-brand);
  font-weight: 700;
  background: var(--brand);
  border-color: var(--brand);
}
.btn.primary:hover { background: var(--brand-strong); border-color: var(--brand-strong); }

.btn.block { width: 100%; }
.btn.small { padding: 6px 11px; font-size: .85rem; font-weight: 500; }
.btn.quiet { background: transparent; border-color: transparent; color: var(--text-dim); }
.btn.quiet:hover { color: var(--text); border-color: var(--line); }
.btn.danger { color: var(--danger); border-color: var(--line); background: transparent; }
.btn.danger:hover { border-color: var(--danger); }

label { display: block; margin: 0 0 6px; font-size: .78rem; color: var(--text-dim); }

/* Bare `input` rather than input[type="text"]: an <input> with no type attribute behaves as text
   but does NOT match [type="text"], because attribute selectors match the attribute and not the
   resolved default. That left half the fields with browser-default white boxes. */
.input,
input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
textarea, select {
  width: 100%;
  padding: 10px 12px;
  font: inherit;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  outline: none;
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.input:focus, input:focus, textarea:focus, select:focus {
  border-color: var(--brand);
  box-shadow: var(--ring);
}

input:disabled, textarea:disabled { opacity: .55; cursor: not-allowed; }

::placeholder { color: var(--text-faint); }
textarea { resize: vertical; min-height: 88px; }

/* Selectable preset tags — orientation, language, gender. */
.tag {
  padding: 6px 12px;
  font-size: .85rem;
  color: var(--text-dim);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  user-select: none;
  transition: all .15s var(--ease);
}
.tag:hover { border-color: var(--brand); color: var(--text); }

/* The "make a free account" card guests see where the site needs one (accountWall in app.js). */
.account-wall { display: grid; gap: 10px; justify-items: center; text-align: center; padding: 22px 16px; }
.account-wall b { font-size: 1.1rem; }
.account-wall p { margin: 0; color: var(--text-dim); }

/* Shown tags (not the pickers), lightly tinted by kind so the three groups read apart:
   gender pink, orientation purple, language teal. */
.tag-gender      { --tag-h: 330; }
.tag-orientation { --tag-h: 270; }
.tag-language    { --tag-h: 185; }
/* Profile badges gold, game numbers blue (progress.js, 2026-10-03). */
.tag-badge       { --tag-h: 42; }
.tag-stat        { --tag-h: 210; }
.tag-gender, .tag-orientation, .tag-language, .tag-badge, .tag-stat {
  color: hsl(var(--tag-h) 70% 80%);
  background: hsl(var(--tag-h) 70% 60% / .13);
  border-color: hsl(var(--tag-h) 70% 62% / .38);
}
[data-theme="light"] .tag-gender, [data-theme="light"] .tag-orientation,
[data-theme="light"] .tag-language, [data-theme="light"] .tag-badge, [data-theme="light"] .tag-stat {
  color: hsl(var(--tag-h) 55% 32%);
  background: hsl(var(--tag-h) 70% 55% / .12);
  border-color: hsl(var(--tag-h) 60% 50% / .35);
}
.tag[aria-pressed="true"] {
  color: var(--on-brand);
  background: var(--brand);
  border-color: var(--brand);
}

.tabs { display: flex; gap: 4px; padding: 3px; background: var(--surface-2); border-radius: var(--r-sm); }
.tabs .btn { flex: 1; background: transparent; border-color: transparent; font-weight: 500; }
.tabs .btn[aria-selected="true"] { background: var(--surface); border-color: var(--line); }

.avatar {
  width: 88px; height: 88px; flex: none;
  object-fit: cover;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 50%;
}

/* ---- navigation ---------------------------------------------------------------------------- */

.nav {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 12px 0 16px;
}
.nav-brand { display: flex; flex: none; margin: -4px 2px -4px 0; border-radius: 10px; }
.nav-brand img { display: block; width: 44px; height: 44px; transition: transform .15s; }
.nav-brand:hover img { transform: scale(1.08) rotate(-4deg); }
.nav-links { display: flex; gap: 2px; flex-wrap: wrap; }
.nav-right { display: flex; gap: 3px; margin-left: auto; }

/* Labelled, not just an icon. An icon-only bar makes people guess, and guessing is exactly what
   was getting players lost. */
.nav-link {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 9px;    /* 9, not 13: VIP (2026-09-30) and 💰 coins (2026-10-11) have to fit on one line at 920 px */
  font: inherit; font-size: .88rem; font-weight: 550;
  color: var(--text-dim); text-decoration: none;
  background: transparent; border: 1px solid transparent; border-radius: var(--r-sm);
  cursor: pointer;
}
.nav-link:hover { color: var(--text); background: var(--surface-2); text-decoration: none; }

/* The page you are on, stated rather than left to work out. */
.nav-link[aria-current="page"] {
  color: var(--text); background: var(--surface-2); border-color: var(--line);
}

.nav-link.primary {
  color: var(--on-brand); background: var(--brand); border-color: var(--brand); font-weight: 700;
}
.nav-link.primary:hover { background: var(--brand-strong); color: var(--on-brand); }

.nav-icon { font-size: .95em; line-height: 1; }

/* Discord in its own colour and mark, so it is spotted rather than read. */
.nav-discord { color: #fff; background: #5865F2; border-color: #5865F2; }
.nav-discord:hover { color: #fff; background: #4752C4; }
.nav-discord svg { display: block; width: 18px; height: 18px; fill: currentColor; }
/* Logo only (2026-10-11), to make room for the 💰 coins pill: the purple mark is known on sight. */
.nav-discord span:not(.nav-icon) { display: none; }
.nav-discord { padding: 8px 10px; }
.nav-unread { margin-left: 2px; }
/* 💰 coins in the top bar (2026-10-11), gold like the coins page. */
.nav-coins { gap: 4px; padding: 8px 7px; color: var(--text); font-weight: 750; font-variant-numeric: tabular-nums;
             background: hsl(45 90% 55% / .14); border-color: hsl(45 90% 55% / .45); }
.nav-coins:hover { background: hsl(45 90% 55% / .24); }
.nav-coins[aria-current="page"] { background: hsl(45 90% 55% / .24); border-color: hsl(45 90% 60% / .8); }

@media (max-width: 620px) {
  /* Stacked, not stripped.
     This used to hide every label, which left five unlabelled icons and exactly the guessing the
     comment above says the bar exists to avoid — on the one screen size where people are least
     able to hover to find out. Putting the name under the icon costs one line of height. */
  .nav { gap: 8px; }
  .nav-brand { order: 1; }
  .nav-brand img { width: 38px; height: 38px; }
  .nav-right { order: 2; margin-left: auto; }
  .nav-links { order: 3; width: 100%; flex-wrap: nowrap; gap: 2px; }

  .nav-links .nav-link {
    position: relative; flex: 1; min-width: 0;
    flex-direction: column; gap: 3px;
    padding: 7px 4px;
    font-size: .64rem; font-weight: 600; line-height: 1.15; text-align: center;
  }
  .nav-links .nav-icon { font-size: 1.1rem; }

  /* Sits on the icon rather than pushing the label around. */
  .nav-links .nav-unread { position: absolute; top: 3px; right: 7px; margin: 0; }

  /* The right-hand cluster is the theme switch and Discord — recognisable marks that do not need
     spelling out, and the room is better spent on the five that navigate. */
  .nav-right .nav-link span:not(.nav-icon):not(.pill) { display: none; }
  /* ...except the coin number: the pill is pointless without it. */
  .nav-right .nav-link.nav-coins span.nav-coins-n { display: inline; }
}

/* ---- people ------------------------------------------------------------------------------- */

.pfp { border-radius: 50%; object-fit: cover; background: var(--surface-2); flex: none; }

.name { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.name b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Online dot after a name (2026-10-05, app.js presenceEl): green = playing or on the site, grey =
   away. The words appear in .presence-tip on hover / tap. Fixed greens, not the brand hue, because
   "green means online" is the convention people already read without thinking. */
.presence { flex: none; width: 9px; height: 9px; border-radius: 50%; cursor: help; outline: none;
            background: hsl(var(--surface-h) 8% 55%); box-shadow: 0 0 0 2px var(--surface); }
.presence.on { background: hsl(145 65% 48%); box-shadow: 0 0 0 2px var(--surface), 0 0 8px hsl(145 70% 50% / .7); }
.presence:focus-visible { box-shadow: 0 0 0 2px var(--surface), var(--ring); }
.presence-tip { position: fixed; z-index: 1000; padding: 5px 10px; font-size: .8rem; font-weight: 600; white-space: nowrap;
                color: var(--text); background: var(--surface-2); border: 1px solid var(--line); border-radius: 8px;
                box-shadow: 0 6px 20px rgb(0 0 0 / .35); pointer-events: none; }

/* Staff and paid tags. Distinct colours because confusing a moderator with a subscriber is the
   one mistake that matters here. */
.badge {
  flex: none;
  padding: 1px 7px;
  font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  border-radius: 999px;
  color: var(--on-brand);
}
/* Owner. Pink, and the only badge that is not in the red/blue/gold family, so it never reads as
   "an admin" at a glance. */
.badge-owner { background: linear-gradient(95deg, hsl(332 88% 62%), hsl(310 78% 58%));
               color: #fff; }
.badge-admin { background: hsl(5 75% 58%); color: #fff; }
.badge-mod   { background: hsl(205 70% 52%); color: #fff; }
/* White and bold on the gold (Rainy, 2026-09-30), with a soft shadow so it reads on the light end. */
.badge-vip   { background: linear-gradient(95deg, hsl(45 95% 60%), hsl(var(--brand-h) 85% 62%));
               color: #fff; font-weight: 800; text-shadow: 0 1px 1px rgb(0 0 0 / .35); }
/* VIP+ (2026-10-05): gold, pink and purple, still. (A sweeping shine and a pulsing glow were tried the same
   day; Rainy: "the looping anim sucks". Keep both still.) */
.badge-vipplus { color: #fff; font-weight: 800; text-shadow: 0 1px 1px rgb(0 0 0 / .35);
                 background: linear-gradient(100deg, hsl(45 95% 58%), hsl(var(--brand-h) 85% 62%) 55%, hsl(275 75% 64%)); }

/* VIP+ ring around the profile picture (app.js avatarEl): gold-to-pink with a soft glow. Box-shadow, so
   it never changes the picture's size or anybody's layout. */
.pfp.plus { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px hsl(45 95% 58%), 0 0 0 5px hsl(var(--brand-h) 85% 62%),
                        0 0 14px 3px hsl(var(--brand-h) 85% 62% / .45); }

/* Gold button for VIP / VIP+ actions (same gold as .nav-vip). */
.btn.gold-btn { color: #fff; font-weight: 700; background: #e3a008; border-color: #e3a008;
                text-shadow: 0 1px 2px rgb(0 0 0 / .35); }
.btn.gold-btn:hover { background: #c98c00; border-color: #c98c00; }

/* A row that is a whole person: picture, name, something on the right. */
.person { display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: var(--r-sm);
          cursor: pointer; border: 1px solid transparent; text-align: left; width: 100%;
          background: transparent; font: inherit; color: inherit; }
.person:hover { background: var(--surface-2); }
.person[aria-selected="true"] { background: var(--surface-2); border-color: var(--line); }
.person .sub { display: block; font-size: .78rem; color: var(--text-dim);
               overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.person .grow { flex: 1; min-width: 0; }

.pill { flex: none; min-width: 20px; padding: 1px 7px; font-size: .72rem; font-weight: 700;
        text-align: center; color: var(--on-brand); background: var(--brand); border-radius: 999px; }

.msg { min-height: 1.3em; margin: 12px 0 0; font-size: .84rem; }
.msg.err { color: var(--danger); }
.msg.ok  { color: var(--ok); }

.chip-count { font-size: .72rem; color: var(--text-faint); }

/* ============================================================================================
   RAINBOW MODE — proof the single-hue design pays off
   Add class="rainbow" to <html> and the entire palette cycles. Nothing else has to change.
   ============================================================================================ */

@property --brand-h {
  syntax: '<number>';
  inherits: true;
  initial-value: 330;
}

@keyframes hue-cycle { to { --brand-h: 690; } }   /* 330 + 360 */

.rainbow { animation: hue-cycle 12s linear infinite; }

@media (prefers-reduced-motion: reduce) {
  .rainbow { animation: none; }
  * { transition: none !important; }
}

/* ---------------------------------------------------------------------------------------------
   Age / terms notice
   A notice and a record of acknowledgement — NOT age verification. See mountAgeGate in app.js.
   --------------------------------------------------------------------------------------------- */

.agegate {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: grid;
  place-items: center;
  padding: 20px;
  /* Opaque, not translucent: the point is that the content behind is not visible until the notice
     has been answered. */
  background: var(--bg);
}

.agegate-panel {
  width: min(460px, 100%);
  display: grid;
  gap: 14px;
  padding: 28px 24px;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}

.agegate-panel h1 { margin: 0; font-size: 1.6rem; }
.agegate-panel p { margin: 0; color: var(--text-dim); }
.agegate-small { font-size: .82rem; color: var(--text-faint); line-height: 1.5; }
.agegate-small a { color: var(--text-dim); }

.agegate-row { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 4px; }
.agegate-row .btn { flex: 1 1 auto; text-decoration: none; display: grid; place-items: center; }

/* ============================================================================================
   ADS — the two side rails (assets/ads.js). Fixed in the empty space either side of the 920 px
   column, so they never cover or push content. Hidden again if the window is narrowed after
   they loaded (an ad cannot resize).
   ============================================================================================ */

.ad-rail { position: fixed; z-index: 1; min-height: 600px; }
/* Out at the screen edges, away from the site's buttons (Rainy, 2026-10-11). ads.js only places them
   when there is room for 160 px plus gaps beside the 920 px column, so they never overlap it. */
.ad-rail.left  { left: 16px; }
.ad-rail.right { right: 16px; }
/* Google reports no inventory with data-ad-status, not data-adsbygoogle-status.
   Keep pending units measurable/visible so AdSense can request an ad; collapse only confirmed
   empty rails. Filled ads and Google's optimized replacements retain their normal display. */
.ad-rail:has(.adsbygoogle[data-ad-status="unfilled"]) { display: none !important; }
@media (max-width: 1335px), (max-height: 647px) { .ad-rail { display: none; } }
/* Phones: one 300x250 under everything (ads.js mountBottomAd). */
.ad-bottom { display: flex; justify-content: center; padding: 28px 16px 36px; }


/* VIP profile banner (perks.js), across the top of a public profile, behind the picture. */
.profile-banner { height: 72px; border-radius: var(--r-md); margin-bottom: -36px; }
.profile-banner + .who-head { padding: 0 14px; align-items: flex-end; }
.profile-banner + .who-head .pfp { border: 3px solid var(--surface); }

/* VIP gradients (perks.js presets, and UiKit.Gradients in the game — keep all three in step).
   One class sets the colours; .name-grad paints text with it, .banner-grad paints a banner. */
.grad-rainbow { --grad: linear-gradient(90deg, #f25454, #f08a24, #cf9a00, #2fb866, #3b82f6, #9b5cf6, #ff5fa2); }
.grad-sunset  { --grad: linear-gradient(90deg, #ff8a3d, #ff5f7e, #b44cff); }
.grad-ocean   { --grad: linear-gradient(90deg, #14c8c8, #2d8cff, #4a5cf0); }
.grad-candy   { --grad: linear-gradient(90deg, #ff6fc0, #c28bff, #6fc8ff); }
.grad-fire    { --grad: linear-gradient(90deg, #e63939, #ff7a1a, #ffc21a); }
.grad-aurora  { --grad: linear-gradient(90deg, #2fd08a, #19b3b3, #8a5cff); }
.grad-galaxy  { --grad: linear-gradient(90deg, #7a4dff, #3b6bff, #ff4fbf); }
.name-grad {
  background: var(--grad);
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
}
.profile-banner.banner-grad { background: var(--grad); }
/* A banner picture: taller than a colour strip, cropped to fill. */
.profile-banner.banner-img { height: 150px; background-size: cover; background-position: center; }

/* Gold VIP buttons (Rainy, 2026-09-30): the nav button and the one under the landing doors. */
.nav-vip, .door.vip-door { color: #fff; font-weight: 700; background: #e3a008; border-color: #e3a008;
                          text-shadow: 0 1px 2px rgb(0 0 0 / .35); }
.nav-vip:hover, .door.vip-door:hover { color: #fff; background: #c98c00; border-color: #c98c00; }
.nav-vip[aria-current="page"] { color: #fff; background: #e3a008; border-color: #fff; }

/* ---- Rainy's list 2026-10-23 ------------------------------------------------------------------ */

/* Profile badges, one row (players.js badgeList): each kind its own colour. The live numbers are
   badges too now. A badge that goes somewhere is a link and lifts a little on hover. */
.badge-chip { --tag-h: 42; color: hsl(var(--tag-h) 75% 80%); background: hsl(var(--tag-h) 75% 58% / .14);
              border-color: hsl(var(--tag-h) 75% 62% / .42); text-decoration: none; }
[data-theme="light"] .badge-chip { color: hsl(var(--tag-h) 60% 30%); background: hsl(var(--tag-h) 75% 55% / .13);
                                   border-color: hsl(var(--tag-h) 60% 48% / .4); }
.badge-chip.b-time    { --tag-h: 210; }
.badge-chip.b-streak  { --tag-h: 22; }
.badge-chip.b-ko      { --tag-h: 0; }
.badge-chip.b-emote   { --tag-h: 285; }
.badge-chip.b-friend  { --tag-h: 145; }
.badge-chip.b-discord { --tag-h: 235; }
.badge-chip.b-early   { --tag-h: 95; }
.badge-chip.b-supporter { color: #fff; font-weight: 700; text-shadow: 0 1px 1px rgb(0 0 0 / .35); border-color: transparent;
                          background: linear-gradient(100deg, #ff424d, #ff7a59 45%, #f6b93b); }
a.badge-chip, a.badge-chip:hover { cursor: pointer; text-decoration: none; transition: transform .12s ease, box-shadow .12s ease; }
a.badge-chip:hover { transform: translateY(-1px); box-shadow: 0 3px 10px hsl(var(--tag-h) 75% 50% / .35); }
a.badge-chip:focus-visible { outline: none; box-shadow: var(--ring); }

/* VIP+ ring styles (app.js avatarEl adds .ring-<id>; perks.js PLUS_THEMES). Still, like the classic one. */
.pfp.plus.ring-rainbow  { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px #f25454, 0 0 0 5px #3b82f6, 0 0 14px 3px rgb(155 92 246 / .5); }
.pfp.plus.ring-fire     { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px #ffc21a, 0 0 0 5px #e63939, 0 0 16px 3px rgb(255 122 26 / .55); }
.pfp.plus.ring-ice      { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px #bdf3ff, 0 0 0 5px #3b9dff, 0 0 16px 3px rgb(120 210 255 / .55); }
.pfp.plus.ring-emerald  { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px #4ee39a, 0 0 0 5px #0f9d6a, 0 0 16px 3px rgb(46 208 138 / .5); }
.pfp.plus.ring-galaxy   { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px #7a4dff, 0 0 0 5px #ff4fbf, 0 0 18px 4px rgb(122 77 255 / .55); }
.pfp.plus.ring-rose     { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px #f7c5b5, 0 0 0 5px #d98a8a, 0 0 14px 3px rgb(240 170 160 / .5); }
.pfp.plus.ring-candy    { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px #ff6fc0, 0 0 0 5px #6fc8ff, 0 0 14px 3px rgb(194 139 255 / .5); }
.pfp.plus.ring-midnight { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px #2b2f6b, 0 0 0 5px #c9b26b, 0 0 14px 3px rgb(80 90 200 / .5); }

/* VIP+ tag styles (app.js badgeEl adds .tagtheme-<id>). */
.badge-vipplus.tagtheme-gold     { background: linear-gradient(100deg, #b8860b, #f4c430 50%, #ffe08a); color: #3a2600; text-shadow: none; }
.badge-vipplus.tagtheme-patreon  { background: linear-gradient(100deg, #ff424d, #ff7a3d 55%, #ffb347); }
.badge-vipplus.tagtheme-rainbow  { background: linear-gradient(100deg, #f25454, #f08a24, #cf9a00, #2fb866, #3b82f6, #9b5cf6); }
.badge-vipplus.tagtheme-fire     { background: linear-gradient(100deg, #e63939, #ff7a1a 55%, #ffc21a); }
.badge-vipplus.tagtheme-ice      { background: linear-gradient(100deg, #3b9dff, #6fd3ff 55%, #b8f0ff); }
.badge-vipplus.tagtheme-emerald  { background: linear-gradient(100deg, #0f9d6a, #2fd08a 55%, #9be86a); }
.badge-vipplus.tagtheme-galaxy   { background: linear-gradient(100deg, #3b2bb8, #7a4dff 50%, #ff4fbf); }
.badge-vipplus.tagtheme-rose     { background: linear-gradient(100deg, #c9776f, #e9a99a 55%, #f4cdb8); }
.badge-vipplus.tagtheme-candy    { background: linear-gradient(100deg, #ff6fc0, #c28bff 55%, #6fc8ff); }
.badge-vipplus.tagtheme-midnight { background: linear-gradient(100deg, #1d2050, #3a3f8f 60%, #c9b26b); }

/* Moving name colours, VIP+ and staff (perks.js ANIMATED_NAME_COLOURS, app.js paintName, and the
   game's UiKit.Animated — keep all three in step). "flow": the colours slide along the name; the
   gradient repeats its first colour at the end so the loop has no seam. "shine": a light sweeps across. */
.anim-flow-rainbow { --grad: linear-gradient(90deg, #f25454, #f08a24, #cf9a00, #2fb866, #3b82f6, #9b5cf6, #ff5fa2, #f25454); }
.anim-flow-fire    { --grad: linear-gradient(90deg, #e63939, #ff7a1a, #ffc21a, #ff7a1a, #e63939); }
.anim-flow-ocean   { --grad: linear-gradient(90deg, #14c8c8, #2d8cff, #4a5cf0, #2d8cff, #14c8c8); }
.anim-flow-candy   { --grad: linear-gradient(90deg, #ff6fc0, #c28bff, #6fc8ff, #c28bff, #ff6fc0); }
.anim-flow-galaxy  { --grad: linear-gradient(90deg, #7a4dff, #3b6bff, #ff4fbf, #3b6bff, #7a4dff); }
.anim-shine-gold   { --base: linear-gradient(90deg, #c98a00, #f4c430, #c98a00); }
.anim-shine-pink   { --base: linear-gradient(90deg, #e0408a, #ff8cc6, #e0408a); }
.anim-shine-ice    { --base: linear-gradient(90deg, #3b9dff, #9fe6ff, #3b9dff); }
.name-anim[class*="anim-flow-"] { background-size: 200% 100%; animation: name-flow 3s linear infinite; }
.name-anim[class*="anim-shine-"] {
  background: linear-gradient(105deg, transparent 38%, rgb(255 255 255 / .95) 50%, transparent 62%) no-repeat,
              var(--base);
  background-size: 250% 100%, 100% 100%;
  -webkit-background-clip: text; background-clip: text;
  animation: name-shine 2.6s ease-in-out infinite;
}
@keyframes name-flow  { from { background-position: 0% 0; } to { background-position: -200% 0; } }
@keyframes name-shine { 0% { background-position: 160% 0, 0 0; } 70%, 100% { background-position: -60% 0, 0 0; } }
@media (prefers-reduced-motion: reduce) { .name-anim { animation: none !important; } }
