/* Strive AU */
:root{
  --bg:#eef2f5; --panel:#ffffff; --panel-edge:#c8d4dc; --ink:#16222b; --ink-dim:#5a6f7d;
  --accent:#8a6d1f; --accent-soft:#c9b458;
  --shadow:0 1px 2px rgba(20,40,55,.10),0 8px 24px rgba(20,40,55,.10);
  --ok:#0072b2; --warn:#8a5a00; --bad:#b83c00; --bad-wash:rgba(213,94,0,.12);
  --discord:#5865f2; --discord-hi:#4752c4; --discord-ink:#5865f2;
  --steam:#3d97cc; --steam-hi:#2f7ba8;
}
@media (prefers-color-scheme:dark){
  :root{
    --bg:#0d1319; --panel:#141d25; --panel-edge:#25333d; --ink:#dde7ee; --ink-dim:#8299a8;
    --accent:#c9b458; --accent-soft:#c9b458;
    --shadow:0 1px 2px rgba(0,0,0,.5),0 10px 30px rgba(0,0,0,.45);
    --ok:#56b4e9; --warn:#ffcf6b; --bad:#ff8a5b; --bad-wash:rgba(255,138,91,.14);
    /* Nav link needs a lighter blurple on the dark panel -- #5865f2 there is only 3.7:1,
       which fails AA for 12px nav text. The button keeps brand blurple; white on it is
       4.6:1 regardless of what surrounds it. */
    --discord:#5865f2; --discord-hi:#4752c4; --discord-ink:#949cf7;
    --steam:#66c0f4; --steam-hi:#8ad0f8;
  }
}
*{box-sizing:border-box}
/* Reserve the scrollbar's lane whether or not the page needs one, so switching
   between a short board and the long all-time one on the leaderboard does not
   shift the centred content sideways. Verified with probe pages: without this a
   1000px viewport centres short pages 7px to the right of long ones. */
html{scrollbar-gutter:stable}
body{margin:0;background:var(--bg);color:var(--ink);
  font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  font-size:15px;line-height:1.55;-webkit-font-smoothing:antialiased}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-variant-numeric:tabular-nums}

nav{display:flex;align-items:center;gap:4px;flex-wrap:wrap;
  padding:10px 18px;background:var(--panel);border-bottom:1px solid var(--panel-edge)}
nav .brand{font-weight:700;letter-spacing:.14em;text-transform:uppercase;font-size:13px;
  color:var(--accent);margin-right:14px;text-decoration:none}
nav a{color:var(--ink-dim);text-decoration:none;font-size:12px;font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;padding:5px 10px;border-radius:2px;
  border:1px solid transparent}
nav a:hover{color:var(--ink);border-color:var(--accent-soft)}
nav a[aria-current="page"]{color:var(--ink);border-color:var(--panel-edge)}
nav .soon{opacity:.45;cursor:default}
nav .soon:hover{color:var(--ink-dim);border-color:transparent}

main{max-width:820px;margin:0 auto;padding:34px 18px 60px}
h1{font-size:26px;margin:0 0 4px;letter-spacing:-.01em}
.sub{color:var(--ink-dim);margin:0 0 30px}
/* The last-updated stamp lives inside .sub on the rules and privacy pages. As an inline span it
   ran on from the end of the sentence and read as an afterthought, which matters on the rules
   page because the appeals clause measures bans against "this page as it stood at the time" --
   so the date is load-bearing, not decoration. Block gives it its own line; .note already
   carries the 14px top margin. */
.sub .note{display:block}
.card{background:var(--panel);border:1px solid var(--panel-edge);border-radius:4px;
  padding:18px 20px;box-shadow:var(--shadow);margin:0 0 18px}
.card h2{font-size:12px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-dim);
  margin:0 0 12px;font-weight:600}
/* A player's name is their identity, so it renders EXACTLY as they set it in game -- BUMBO
   stays BUMBO and LaFitch stays LaFitch. `.card h2` uppercases every other heading on the
   site and was winning on specificity, so this has to out-specify it rather than sit beside
   it: plain `.name` ties with `.card h2` and only won by source order, which is why the rule
   below is scoped to the card. Nothing here may re-case user-supplied text. */
.card h2.name{font-size:17px;font-weight:600;margin:0 0 12px;word-break:break-word;
  text-transform:none;letter-spacing:normal;color:var(--ink)}
dl{display:grid;grid-template-columns:auto 1fr;gap:6px 18px;margin:0}
dt{color:var(--ink-dim);font-size:12px;letter-spacing:.08em;text-transform:uppercase;
  font-weight:600;padding-top:2px}
dd{margin:0}
.tag{display:inline-block;font-size:11px;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;padding:2px 7px;border-radius:2px;
  border:1px solid var(--panel-edge);color:var(--ink-dim)}
.tag.warn{color:var(--warn);border-color:var(--warn)}

/* The live-state marker on a server tile: a dot and bare text in the ok colour, so it
   reads as status while the bordered chips (Play, the address) read as things to click.
   The ok token is Okabe-Ito sky blue, safe for the owner's deuteranopia. */
.status-live{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;
  letter-spacing:.06em;text-transform:uppercase;color:var(--ok)}
.status-live .dot{width:7px;height:7px;border-radius:50%;background:var(--ok)}
/* Live values filled in by status.js. Tabular figures so a count ticking 9 -> 10 does not
   shift the row, and a fixed min-width so the placeholder does not visibly jump when the
   real value lands. Colour separates by LIGHTNESS and never by hue alone -- red/green is
   unreadable here -- and every state is also spelled out in words, so the colour is
   reinforcement rather than the message. */
.live{font-variant-numeric:tabular-nums;min-width:5ch;display:inline-block}
.live-online{color:var(--ok);font-weight:600}
.live-offline{color:var(--warn)}
.live-unknown{color:var(--ink-dim)}
/* The queue, appended after the count as "40 / 40 + 10". Deliberately quieter than the count
   itself: it is secondary information, and at the same weight it reads as part of the number
   and makes the tile look like it is reporting 50 players on a 40-slot server. */
.live-queue{color:var(--ink-dim);font-weight:400}
.note{color:var(--ink-dim);font-size:13px;margin:14px 0 0}
/* The connect address. It carries the connect story alone now that the steam:// button has
   gone; see index.html.
   NO `user-select:all` here: that is a browser behaviour where a single click selects the
   whole element, so with click-to-copy it highlighted the address on every click for no
   reason. Ordinary selection still works if someone wants to drag over it. */
.addr{background:var(--bg);border:1px solid var(--panel-edge);border-radius:3px;
  padding:1px 7px;font-size:13px;cursor:pointer}
.addr:hover{border-color:var(--accent-soft)}
/* It is exposed as role="button", so it must show a focus ring for keyboard users -- the
   pointer affordance above does nothing for them. */
.addr:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
/* The copy confirmation. A separate element rather than swapping the address text: on the
   fallback path the address IS selected, and rewriting it mid-selection is how "Copied" ends
   up on someone's clipboard instead of the address. */
.copied{margin-left:8px;font-size:12px;color:var(--ok);font-weight:600}
.lead{margin:0 0 16px}

/* ---- bands ------------------------------------------------------------------------------
   The homepage is a stack of full-bleed bands rather than a column of identical cards, so each
   section reads as a deliberate step instead of another box. Grounds alternate to give rhythm.
   Inner width is per-band: prose keeps the 820px measure the rest of the site reads at, while
   .band-in gets 1120px because it holds a grid that has to grow sideways. */
.band{background:var(--bg)}
.band-alt{background:var(--panel);border-block:1px solid var(--panel-edge)}
/* ONE width for every band. Earlier the servers band was wider than the prose bands and their
   left edges did not line up down the page, which read as sloppy. Prose is held to a readable
   measure by max-width on the text itself instead, so alignment and readability stop competing. */
.band-in{max-width:1120px;margin:0 auto;padding:40px 18px}
.band-in > p,.band-in > .band-h + p{max-width:64ch}
.band-h{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-dim);
  margin:0 0 16px;font-weight:600}

/* ---- hero -------------------------------------------------------------------------------
   Background is flat on purpose: there is no artwork yet. Adding one later means background-image
   on .band-hero plus a scrim behind its .band-in so body text keeps contrast, and no markup
   change. The hero holds the pitch ONLY -- servers used to live here and made the hero grow
   taller with every one added, which is why they are their own band now. */
.band-hero{background:var(--panel);border-bottom:1px solid var(--panel-edge)}
.band-hero .band-in{padding:52px 18px 46px}
.eyebrow{font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--accent);
  font-weight:600;margin:0 0 9px}
.band-hero h1{font-size:44px;line-height:1.04;margin:0 0 14px;letter-spacing:-.022em}
.hero-sub{margin:0 0 22px;color:var(--ink-dim);max-width:52ch;font-size:17px}
.hero-cta{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
/* Secondary action, deliberately quiet: the Discord button is the only conversion that works
   while the server is closed, so nothing beside it should compete for the same weight. */
.btn-ghost{display:inline-flex;align-items:center;justify-content:center;padding:0 16px;
  height:40px;border:1px solid var(--panel-edge);border-radius:4px;color:var(--ink);
  text-decoration:none;font-size:14px;font-weight:600;letter-spacing:.02em;white-space:nowrap}
.btn-ghost:hover{border-color:var(--accent-soft)}
.btn-ghost:focus-visible{outline:2px solid var(--ink);outline-offset:2px}

/* ---- servers ------------------------------------------------------------------------------
   auto-fit, so a third and fourth server wrap into the grid with no layout work. The 300px floor
   puts three across the 1120px band; below that they fall to two and then one. */
/* Natural heights, not stretched. A placeholder forced to match a live server's height is just
   a large empty box, and an honestly short card reads as "less here yet" rather than broken. */
.srv-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:14px;
  align-items:start}
.srv{background:var(--bg);border:1px solid var(--panel-edge);border-radius:4px;padding:16px 18px}
.band-alt .srv{background:var(--panel)}
.srv-top{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:0 0 7px}
.srv-map{font-size:16px;font-weight:600;margin:0;letter-spacing:0;text-transform:none}
.srv-name{font-size:11.5px;color:var(--ink-dim);margin:0 0 12px;word-break:break-word}
.srv-stats{display:grid;grid-template-columns:repeat(3,auto);gap:0 20px;margin:0 0 12px;
  justify-content:start}
.srv-stats dt{font-size:10px;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-dim);
  font-weight:600;margin:0 0 2px}
.srv-stats dd{margin:0;font-size:14px;font-weight:600}
.srv-connect{margin:0}
.srv .note{margin:12px 0 0}
/* A promise, not an offer. It must not read as something a player can act on today. */
.srv-soon{border-style:dashed;opacity:.72}
.srv-soon-copy{margin:0;font-size:13px;color:var(--ink-dim)}

/* ---- leaderboard strip --------------------------------------------------------------------- */
.band-strip .band-in{padding:24px 18px}
.strip{display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap}
.strip .band-h{margin:0 0 4px}
.strip-copy{margin:0;color:var(--ink-dim);font-size:14px;max-width:60ch}

/* main is now only the footer wrapper on this page; the bands set their own widths. */
.flow{max-width:1120px;margin:0 auto;padding:0 18px}

@media (max-width:720px){
  .band-hero .band-in{padding:34px 18px 30px}
  .band-hero h1{font-size:33px}
  .hero-sub{font-size:16px}
  .band-in{padding:30px 18px}
}
/* The "Why Strive" grid. Deliberately borrows .tile's visual language (same --bg inset, border
   and radius) so it reads as part of the same system rather than a second card style, but it is
   a separate class because .tile is built around a big numeric value and these are prose. The
   240px floor puts all three across the 784px content column, which is the layout this is tuned
   for; it falls to one column on a phone. Between roughly 530 and 810px of viewport it lands as
   2 + 1, which is accepted rather than fixed: a new breakpoint for a transient band is not worth
   it when the file otherwise has exactly one. Prose needs a wider measure than .tile's 128px
   numbers do, which is the whole reason this is not just .tiles. */
.feats{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:10px}
.feat{background:var(--bg);border:1px solid var(--panel-edge);border-radius:3px;padding:12px 14px}
.feat-h{font-size:12px;letter-spacing:.07em;text-transform:uppercase;color:var(--ink);
  font-weight:600;margin:0 0 5px}
.feat p{margin:0;font-size:13px;color:var(--ink-dim);line-height:1.5}
footer{color:var(--ink-dim);font-size:12px;border-top:1px solid var(--panel-edge);
  padding-top:14px;margin-top:26px}
/* Body-text links. These looked unstyled because they only ever set a colour: with no
   text-decoration rule they kept the browser's default underline, which is heavier and sits
   tighter to the text than anything else here.

   The underline STAYS rather than being removed. A link inside a paragraph needs to be
   identifiable as one, and colour alone is a poor signal -- particularly for a red/green
   colour vision deficiency, where a link distinguished only by hue can vanish. So it is kept
   but quietened: panel-edge coloured, offset clear of the descenders, and brightening to the
   link colour on hover. */
a.inline{color:var(--ok);text-decoration:none;font-weight:500}
a.inline:hover{text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px}

/* Standalone "go and look at this" links, as opposed to one sitting mid-sentence. The arrow is
   what makes them read as deliberate rather than as a default browser link, and it nudges on
   hover so the whole thing feels like a control.

   The arrow is CSS rather than text so it cannot end up copied into a quote or a search box,
   and it is decorative: the link text already says where it goes. */
a.inline-go::after{content:"\2009\2192";display:inline-block;transition:transform .12s}
a.inline-go:hover::after{transform:translateX(3px)}
a.inline-go:hover{text-decoration:none}

/* Discord. Brand blurple rather than --accent, because recognition is the whole point of
   this control; white-on-#5865F2 measures 4.6:1, so it clears AA for normal text and is
   comfortably past the 3:1 a button of this size needs. */
.btn{display:inline-flex;align-items:center;gap:9px;
  background:var(--discord);color:#fff;text-decoration:none;
  font-size:14px;font-weight:600;letter-spacing:.02em;
  padding:10px 18px;border-radius:3px;border:1px solid transparent;
  transition:background .12s ease}
.btn:hover{background:var(--discord-hi)}
.btn:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.btn-ico{width:19px;height:19px;flex:none}

nav a.discord{color:var(--discord-ink);border-color:transparent}
nav a.discord:hover{color:var(--discord-ink);border-color:var(--discord-ink)}

@media (max-width:520px){
  dl{grid-template-columns:1fr;gap:2px 0}
  dt{padding-top:8px}
}

/* --- leaderboard & players ------------------------------------------------------------
   Ranking colour comes from LIGHTNESS and a border, never hue alone: the palette here is
   deuteranope-safe by construction (--ok is Okabe-Ito blue, --warn amber) and a green/red
   "good/bad" pair would be unreadable. Top-three emphasis is weight plus a rule, so it
   survives being read in greyscale. */
.lb-controls{display:flex;gap:6px;flex-wrap:wrap;margin:0 0 14px}
.lb-tab{background:transparent;border:1px solid var(--panel-edge);color:var(--ink-dim);
  font:600 11px/1 inherit;letter-spacing:.08em;text-transform:uppercase;
  padding:6px 11px;border-radius:2px;cursor:pointer}
.lb-tab:hover{color:var(--ink);border-color:var(--accent-soft)}
.lb-tab.on{color:var(--ink);border-color:var(--accent);background:var(--bg)}
.lb-tab:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
/* "resets on" caption for the Monthly and Weekly views. Pulled up under the tabs it belongs to,
   pushed off the table so it reads as a label for the tab, not a footnote to the rows. */
.lb-range{margin:-6px 0 14px}

/* The table scrolls inside its own box; the page body must never scroll sideways. */
.lb-scroll{overflow-x:auto}
table.lb{border-collapse:collapse;width:100%;font-size:14px}
table.lb th,table.lb td{padding:7px 10px;text-align:left;white-space:nowrap}
table.lb th{font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-dim);font-weight:600;border-bottom:1px solid var(--panel-edge);
  cursor:pointer;user-select:none}
table.lb th:hover{color:var(--ink)}
table.lb th.on{color:var(--accent)}
table.lb th:focus-visible{outline:2px solid var(--ink);outline-offset:-2px}
table.lb td{border-bottom:1px solid var(--panel-edge)}
table.lb tbody tr:last-child td{border-bottom:0}
/* MUST be scoped to table.lb to win. `table.lb th, table.lb td` above sets text-align:left
   at specificity (0,1,2); a bare `.lb-num` is (0,1,0) and loses, so the numbers silently
   stayed left-aligned. Caught by rendering the page, not by reading the file -- the same
   specificity trap that has bitten a slider elsewhere in this project. */
table.lb .lb-num{text-align:right}
table.lb .lb-rank{color:var(--ink-dim);width:2.5em;text-align:right;
  font-variant-numeric:tabular-nums}
.lb-top .lb-rank{color:var(--accent);font-weight:700}
.lb-top .lb-name{font-weight:600}
.lb-name{color:var(--ink);text-decoration:none}
.lb-name:hover{color:var(--accent);text-decoration:underline}
table.lb .tag{margin-left:8px;font-size:10px}

/* Profile tiles. auto-fit so a narrow screen reflows instead of overflowing. */
.tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(128px,1fr));gap:10px;
  margin:0 0 18px}
.tile{background:var(--bg);border:1px solid var(--panel-edge);border-radius:3px;padding:11px 13px}
.tile-v{font-size:19px;font-weight:600;line-height:1.2}
.tile-k{font-size:11px;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-dim);
  margin-top:3px;font-weight:600}
.tile-s{font-size:10px;color:var(--ink-dim);opacity:.75;margin-top:1px}

.psearch{width:100%;padding:9px 11px;font-size:14px;border-radius:3px;
  border:1px solid var(--panel-edge);background:var(--bg);color:var(--ink)}
.psearch:focus-visible{outline:2px solid var(--ink);outline-offset:1px}

/* The way out of a profile. Sits above the player name, so it needs to read as navigation
   without competing with the h2 -- dim until hovered, same as the board's row links.
   .plist/.prow lived here until 2026-08-16: search used to render its own list of names on
   a separate page, and now it filters the board in place, so both are gone. */
.lb-back{display:inline-block;margin:0 0 14px;font-size:12px;color:var(--ink-dim);
  text-decoration:none}
.lb-back:hover{color:var(--accent)}
.lb-back:focus-visible{outline:2px solid var(--ink);outline-offset:2px}

/* Visually hidden, still read by screen readers -- the search input needs a real label
   even though the placeholder makes its purpose obvious to sighted users. */
.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* Account panel. .btn already exists and is reused for the Steam sign-in; these are the two
   it does not cover. Kept deliberately plain: this panel is a settings control, not a
   call to action, and it should not compete with the board for attention. */
.btn-quiet{display:inline-block;margin-top:14px;background:none;border:1px solid var(--panel-edge);
  color:var(--ink-dim);border-radius:4px;padding:7px 13px;font:inherit;font-size:13px;cursor:pointer}
.btn-quiet:hover{color:var(--ink);border-color:var(--ink-dim)}
.optrow{display:flex;align-items:center;gap:9px;margin:9px 0;font-size:13px;color:var(--ink-dim)}
.optrow label{cursor:pointer}
.optrow input{cursor:pointer;accent-color:var(--accent,#0072B2)}

/* Nav auth links. BOTH are in the markup; this picks one, driven by the `signed-in` class that
   nav-state.js puts on <html> before the page paints. Doing it in CSS rather than by inserting
   an element is what removes the flash: there is nothing to add to the page later.

   SIGNED OUT IS THE DEFAULT, deliberately -- with JavaScript disabled the class is never set,
   and a visitor gets a working Sign in link instead of an empty gap. */
.auth-in{display:none}
html.signed-in .auth-in{display:inline-block}
html.signed-in .auth-out{display:none}

/* NO COLOUR RULE FOR THE AUTH LINKS, DELIBERATELY. They inherit `nav a` like every other item.
   The nav's existing scheme is that colour says what a thing IS, not how important it is:
   --accent gold is the Strive brand, blurple is Discord's, bright-plus-border is the page you
   are on, and everything else is dim. These briefly had gold, which quietly made the accent
   mean two different things and also stopped "My profile" going bright when it was the current
   page, because the colour override beat the aria-current rule.
 */

/* Valve's official sign-in image. Given its own class rather than reusing .btn so no padding,
   background or border is painted behind somebody else's brand asset. Fixed intrinsic size,
   set on the element too, so the card does not reflow while the image loads. */
.steam-btn{display:inline-block;margin-top:6px;line-height:0}
.steam-btn img{display:block;border-radius:3px}
.steam-btn:focus-visible{outline:2px solid var(--accent-soft);outline-offset:3px}

/* Sign-in provider buttons.
 *
 * OUR OWN BUTTONS, NOT VALVE'S SUPPLIED IMAGE. That image is a light, skeuomorphic control from
 * a much older design era and sat on this dark flat page like a sticker. Nothing requires it --
 * Valve offers it as a convenience, not a condition -- and naming the service in the label is
 * ordinary descriptive use. No logo is drawn here: an approximation of somebody else's mark
 * would be worse than none, so the wording carries the meaning.
 *
 * THE TWO ARE SEPARATED BY LIGHTNESS, NOT ONLY HUE. Steam's light blue against Discord's
 * blurple stays distinguishable without relying on colour vision to tell two blues apart.
 */
.provider{display:inline-flex;align-items:center;justify-content:center;gap:9px;width:100%;
  max-width:280px;text-decoration:none;font-size:14px;font-weight:600;letter-spacing:.02em;
  padding:11px 18px;border-radius:3px;border:1px solid transparent;font-family:inherit;
  cursor:pointer;margin:0 0 10px}
.provider-steam{background:var(--steam);color:#0b1116}
.provider-steam:hover{background:var(--steam-hi)}
.provider-discord{background:var(--discord);color:#fff}
.provider-discord:hover{background:var(--discord-hi)}

/* Disabled is a real <button disabled>, so it is unfocusable and unclickable rather than merely
   looking inert. Kept visible on purpose: it tells you the second route is coming, where hiding
   it would just look like Discord was never planned. */
.provider:disabled{opacity:.4;cursor:default}
.provider:disabled:hover{background:var(--discord)}
.provider-note{margin:0 0 18px;font-size:12px;color:var(--ink-dim)}

/* Discord's label swaps to "Coming soon" while hovered. The swap lives on the wrapper because
   a disabled button does not reliably fire hover; see the comment where it is built. */
.provider-slot{display:inline-flex;width:100%;max-width:280px}
.provider-slot .provider{width:100%}
.lbl-hover{display:none}
.provider-slot:hover .lbl-idle{display:none}
.provider-slot:hover .lbl-hover{display:inline}

/* Sign out, waiting for a second click. Deliberately louder than the resting state so it is
   obvious something is pending rather than that the click was missed. */
.btn-quiet.armed{color:var(--warn);border-color:var(--warn)}

/* Join steps. The number is decorative only -- it is aria-hidden and the <ol> already
   carries the ordering for a screen reader, so it is not read out twice. */
.band-lede{color:var(--ink-dim);margin:0 0 22px;max-width:60ch}
.join{list-style:none;margin:0;padding:0;display:grid;gap:18px;
  grid-template-columns:repeat(auto-fit,minmax(290px,1fr))}
.join-step{display:flex;gap:14px;align-items:flex-start;
  background:var(--panel);border:1px solid var(--panel-edge);border-radius:10px;
  padding:18px;box-shadow:var(--shadow)}
.join-n{flex:none;width:28px;height:28px;border-radius:50%;
  display:grid;place-items:center;font-weight:700;font-size:14px;
  background:var(--accent);color:var(--bg);font-variant-numeric:tabular-nums}
.join-body{min-width:0}
.join-h{margin:0 0 6px;font-size:16px}
.join-body p{margin:0 0 12px}
.join-body .note{margin:10px 0 0;font-size:12.5px;color:var(--ink-dim)}
/* Steam blue rather than the Discord blurple .btn defaults to -- this action leaves for
   Steam, and the token already exists for the sign-in button. */
.btn-steam{background:var(--steam);color:#08131b}
.btn-steam:hover{background:var(--steam-hi)}

/* The tile's link down to the join steps. Ghost button so it reads as secondary to the
   address above it, which is still the thing most people came for. */
.srv-cta{margin:12px 0 0}


/* Tile join button: the same quiet chip treatment as the Live tag beside it, in
   the body sans -- the owner's calls, 28/08/2026. The label is PLAY, not JOIN,
   because the sans cap J descends below the baseline (Noto and Segoe both) and
   reads as lowercase at chip size, and mono turned the O into a zero. Diagnosed
   with a glyph specimen; PLAY/LAUNCH/CONNECT all render clean, PLAY chosen. */
.btn-join{display:inline-flex;align-items:center;background:var(--bg);
  border:1px solid var(--panel-edge);border-radius:3px;padding:1px 9px;
  color:var(--ink);text-decoration:none;font-size:12px;font-weight:600;
  letter-spacing:.06em;text-transform:uppercase;white-space:nowrap}
.btn-join:hover{border-color:var(--accent-soft)}
.btn-join:focus-visible{outline:2px solid var(--ink);outline-offset:2px}

/* Title and join chip sit together on the left; the Live tag keeps the right corner. */
.srv-top-left{display:inline-flex;align-items:center;gap:12px}

/* The Current life / Career control on the profile. Two real buttons (keyboard, focus ring
   for free) styled as one segmented bar; the selected half is the only coloured thing, and it
   is marked by an underline as well as the wash so the state never rests on hue alone. */
.seg{display:inline-flex;border:1px solid var(--panel-edge);border-radius:3px;overflow:hidden;margin:0 0 12px}
.seg button{appearance:none;background:none;border:0;border-right:1px solid var(--panel-edge);color:var(--ink-dim);
  font:inherit;font-size:12px;letter-spacing:.08em;text-transform:uppercase;font-weight:600;padding:7px 14px;cursor:pointer}
.seg button:last-child{border-right:0}
.seg button.on{color:var(--ink);background:var(--bg);box-shadow:inset 0 -2px 0 currentColor}

/* The banned notice on the profile. Vermillion, not pure red: the palette rule (see .live-*)
   is Okabe-Ito so it stays readable for a deuteranope, and the words carry the meaning -- the
   colour only says "look here first". One block, above everything, no icon to misread. */
.banner-ban{border:1px solid var(--bad);border-left-width:4px;background:var(--bad-wash);color:var(--ink);
  border-radius:3px;padding:12px 16px;margin:0 0 18px;font-size:14px;line-height:1.5}
.banner-ban strong{color:var(--bad);display:block;margin-bottom:2px}
.banner-ban a{color:var(--bad)}
/* The locked privacy value while banned: one line, no control, dimmed but legible. */
.locked-choice{color:var(--ink-dim)}

