/*
  servermod.app - the community portal.

  TWO MODES, AND THE DEFAULT IS THE SYSTEM'S
  `:root` carries dark, because the product is dark and most people arrive from
  Discord. A visitor whose system asks for light gets light; a visitor who
  presses the toggle gets what they pressed, because `[data-theme]` on <html>
  beats the media query. There is no JavaScript on this site (`script-src
  'none'`), so the toggle is a link the server answers with a cookie - see
  themeFromRequest in index.js.

  FIVE THEMES, SHARED WITH WRAPPED
  `[data-portal-theme]` on <html> swaps typography and surface character only,
  never the anatomy - a member who can read one community's board can read any.
  modern / playful / classic are the vibes util/wrappedCards.js already ships
  for a server's Wrapped cards, so a community's cards and its portal can look
  like the same place. cosy and medieval are new.

  THE ACCENTS ARE NOT DECORATION
  Four hues carry identity across the site (board, achievements, profile, the
  landing cards). Each mode has its own steps rather than one set dimmed: both
  sets were run through a colourblind validator and pass on lightness band,
  chroma floor, CVD separation, normal-vision separation and contrast. If you
  change one, re-run it - adjacent pairs are what break first.

  Copied from dashboard.css by hand rather than shared, for the reason given
  there: this origin must render without any instance being reachable.
*/
:root {
  /* surfaces - dark */
  --sm-bg-base: #0e1116;
  --sm-bg-raised: #181d27;
  --sm-bg-deep: #11151d;
  --sm-bg-hover: #1e2530;
  --sm-text: #e8ecf2;
  --sm-text-muted: #aab3c2;
  --sm-text-dim: #79839a;
  --sm-text-faint: #4c5566;
  --sm-border: rgba(255, 255, 255, 0.10);
  --sm-border-strong: rgba(255, 255, 255, 0.16);
  --sm-skeleton: #232a36;

  /* the four identity hues - dark steps */
  --sm-amber: #bd8420;
  --sm-teal: #1c9dac;
  --sm-rose: #d75e78;
  --sm-violet: #9070e0;

  --sm-accent: var(--sm-teal);
  --sm-accent-hover: #46b8c6;
  --sm-accent-fg: #06181a;
  --sm-warning: var(--sm-amber);
  --sm-success: #6fae74;

  /* medals stay warm metal in both modes */
  --sm-gold: #d4b062;
  --sm-silver: #b9c0cc;
  --sm-bronze: #b58150;

  --sm-font-ui: Archivo, "Hanken Grotesk", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --sm-font-display: var(--sm-font-ui);
  --sm-font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", "Menlo", "Consolas", monospace;

  /* how hard the display type is set - a theme's main lever after colour */
  --sm-display-weight: 800;
  --sm-display-tracking: -0.035em;
  --sm-heading-weight: 700;
}

/* Light: the system's preference, unless the visitor overrode it. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --sm-bg-base: #f4f1ea;
    --sm-bg-raised: #ffffff;
    --sm-bg-deep: #eae5db;
    --sm-bg-hover: #f7f4ee;
    --sm-text: #16191f;
    --sm-text-muted: #4a5260;
    --sm-text-dim: #767f8f;
    --sm-text-faint: #9aa2af;
    --sm-border: rgba(0, 0, 0, 0.12);
    --sm-border-strong: rgba(0, 0, 0, 0.18);
    --sm-skeleton: #ded8cd;

    --sm-amber: #946200;
    --sm-teal: #0090a0;
    --sm-rose: #c72a50;
    --sm-violet: #6a3fd0;

    --sm-accent-hover: #00707d;
    --sm-accent-fg: #ffffff;
    --sm-gold: #9a7414;
    --sm-silver: #6b7280;
    --sm-bronze: #8a5a2b;
    --sm-success: #2f7a44;
  }
}

/* And the same again for an explicit choice, which beats the media query. */
:root[data-theme="light"] {
  --sm-bg-base: #f4f1ea;
  --sm-bg-raised: #ffffff;
  --sm-bg-deep: #eae5db;
  --sm-bg-hover: #f7f4ee;
  --sm-text: #16191f;
  --sm-text-muted: #4a5260;
  --sm-text-dim: #767f8f;
  --sm-text-faint: #9aa2af;
  --sm-border: rgba(0, 0, 0, 0.12);
  --sm-border-strong: rgba(0, 0, 0, 0.18);
  --sm-skeleton: #ded8cd;

  --sm-amber: #946200;
  --sm-teal: #0090a0;
  --sm-rose: #c72a50;
  --sm-violet: #6a3fd0;

  --sm-accent-hover: #00707d;
  --sm-accent-fg: #ffffff;
  --sm-gold: #9a7414;
  --sm-silver: #6b7280;
  --sm-bronze: #8a5a2b;
  --sm-success: #2f7a44;
}

/* ============================== The themes ==============================
   Type and character only. Every theme keeps the same grid, the same columns
   and the same square corners, so the pages stay recognisably one product. */
:root[data-portal-theme="cosy"] {
  --sm-font-ui: Alegreya, Georgia, "Iowan Old Style", serif;
  --sm-font-display: var(--sm-font-ui);
  --sm-display-weight: 800;
  --sm-display-tracking: -0.012em;
}
:root[data-portal-theme="medieval"] {
  --sm-font-ui: "Cormorant Garamond", Garamond, Georgia, serif;
  --sm-font-display: UnifrakturMaguntia, "Cormorant Garamond", Garamond, serif;
  --sm-display-weight: 400;
  --sm-display-tracking: 0;
  --sm-heading-weight: 600;
}
:root[data-portal-theme="playful"] {
  --sm-font-ui: "Baloo 2", "Hanken Grotesk", system-ui, sans-serif;
  --sm-font-display: var(--sm-font-ui);
  --sm-display-weight: 800;
  --sm-display-tracking: -0.01em;
}
:root[data-portal-theme="classic"] {
  --sm-font-ui: "Playfair Display", Georgia, serif;
  --sm-font-display: var(--sm-font-ui);
  --sm-display-weight: 800;
  --sm-display-tracking: -0.02em;
}

/*
  NO ROUNDED CORNERS, ANYWHERE.

  A square is the design language, not a default nobody got round to changing:
  squared avatars, squared tiles, squared buttons. Declared once and globally so
  a new component cannot quietly arrive with a radius on it.
*/
*, *::before, *::after { border-radius: 0 !important; }

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--sm-font-ui);
  background: var(--sm-bg-base);
  color: var(--sm-text);
  font-size: 16px;
  line-height: 1.6;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}
main { flex: 1 0 auto; }

a { color: var(--sm-accent); text-decoration: none; }
a:hover { color: var(--sm-accent-hover); text-decoration: underline; }
a:focus-visible { outline: 1px solid var(--sm-accent); outline-offset: 2px; }

h1 {
  margin: 0;
  font-family: var(--sm-font-display);
  font-size: clamp(1.9rem, 5vw, 2.75rem);
  font-weight: var(--sm-display-weight);
  letter-spacing: var(--sm-display-tracking);
  line-height: 1.1;
}
h2, h3 { font-weight: var(--sm-heading-weight); }
h2.section-title {
  margin: 2.1rem 0 0.85rem;
  font-size: 0.82rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--sm-text-dim);
  display: flex; align-items: center; gap: 0.6rem;
}
/* Medieval sets its display face at a size the blackletter can be read at. */
:root[data-portal-theme="medieval"] h1 { font-size: clamp(2.1rem, 5.4vw, 3rem); line-height: 1.15; }
.muted { color: var(--sm-text-muted); }
.small { font-size: 0.86rem; }
.mt-2 { margin-top: 1.25rem; }

/* ================================ Hero ================================= */
.hero {
  padding: 3.5rem 1.5rem 2.5rem;
  border-bottom: 1px solid var(--sm-border);
  background:
    radial-gradient(80rem 26rem at 50% -12rem, rgba(28, 157, 172, 0.13), transparent 70%),
    var(--sm-bg-base);
}
.hero-inner { max-width: 62ch; margin: 0 auto; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 0.85rem; }
.hero .lede { margin: 0; color: var(--sm-text-muted); }
.guild-icon { width: 88px; height: 88px; object-fit: cover; border: 1px solid var(--sm-border-strong); }

/* =============================== Panels ================================ */
.panel { max-width: 860px; margin: 2rem auto; padding: 0 1.25rem 2rem; }
.panel-narrow { max-width: 60ch; text-align: center; }
.panel-narrow h1 { margin-bottom: 0.75rem; }

.notice { padding: 0.75rem 1rem; border: 1px solid var(--sm-border-strong); background: var(--sm-bg-deep); margin: 0 0 1.25rem; }
.notice-warn { border-color: rgba(207, 159, 78, 0.4); color: var(--sm-warning); }

/* ============================= Directory =============================== */
.community-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.community-row {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) auto auto;
  align-items: center; gap: 0.85rem;
  padding: 0.8rem 1.1rem;
  background: var(--sm-bg-raised);
  border: 1px solid var(--sm-border);
  color: var(--sm-text);
}
.community-row:hover { background: var(--sm-bg-hover); border-color: var(--sm-border-strong); text-decoration: none; }
.community-icon { width: 36px; height: 36px; object-fit: cover; background: var(--sm-bg-deep); }
.community-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.community-count { color: var(--sm-text-dim); font-size: 0.82rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.community-go { color: var(--sm-text-dim); }
@media (max-width: 480px) { .community-count { display: none; } }

/* =============================== Board ================================= */
.board { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.board-link {
  display: grid;
  grid-template-columns: 3.25rem 40px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.9rem;
  padding: 0.6rem 1rem;
  background: var(--sm-bg-raised);
  border: 1px solid var(--sm-border);
  color: var(--sm-text);
}
a.board-link:hover { background: var(--sm-bg-hover); border-color: var(--sm-border-strong); text-decoration: none; }
.board-link-static { cursor: default; }

.rank { font-family: var(--sm-font-mono); font-size: 1rem; color: var(--sm-text-dim); text-align: right; font-variant-numeric: tabular-nums; }
.rank-1 { color: var(--sm-gold); font-weight: 600; }
.rank-2 { color: var(--sm-silver); font-weight: 600; }
.rank-3 { color: var(--sm-bronze); font-weight: 600; }

.avatar { width: 40px; height: 40px; object-fit: cover; background: var(--sm-bg-deep); }
.avatar-fallback { display: grid; place-items: center; font-weight: 600; color: var(--sm-text-dim); border: 1px solid var(--sm-border); }

.who { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.level { color: var(--sm-text-muted); font-size: 0.86rem; white-space: nowrap; }
.xp { font-family: var(--sm-font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.xp-unit { color: var(--sm-text-dim); font-size: 0.8rem; }

/* The level column is the first thing to go: it is derivable from the XP beside
   it, and a name that has to be truncated to make room for a restatement is the
   wrong trade on a phone. */
@media (max-width: 560px) {
  .board-link { grid-template-columns: 2.5rem 32px minmax(0, 1fr) auto; gap: 0.65rem; padding: 0.55rem 0.75rem; }
  .avatar { width: 32px; height: 32px; }
  .level { display: none; }
}

.pager { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-top: 1.25rem; }
.pager-btn { padding: 0.45rem 0.9rem; border: 1px solid var(--sm-border-strong); color: var(--sm-accent); }
a.pager-btn:hover { background: var(--sm-bg-hover); text-decoration: none; }
.pager-off { color: var(--sm-text-dim); border-color: var(--sm-border); }
.pager-count { color: var(--sm-text-dim); font-size: 0.86rem; font-variant-numeric: tabular-nums; }

/* ============================== Member ================================= */
.crumb { margin: 0 0 1.25rem; }
.profile-card { display: block; width: 100%; height: auto; border: 1px solid var(--sm-border); background: var(--sm-bg-deep); }

/* The header shown INSTEAD of the card, on a guild that publishes a board with
   profile cards switched off. Deliberately plain: it stands in for a picture,
   and the facts underneath are the same either way. */
.member-head { display: flex; align-items: center; gap: 1rem; }
.member-avatar { width: 72px; height: 72px; object-fit: cover; background: var(--sm-bg-deep); }
.member-name { margin: 0; font-size: 1.5rem; letter-spacing: -0.02em; }

.facts { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 1.25rem 0 0; padding: 0; }
.facts > div { flex: 1 1 8rem; padding: 0.7rem 0.9rem; background: var(--sm-bg-raised); border: 1px solid var(--sm-border); }
.facts dt { margin: 0; font-size: 0.75rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--sm-text-dim); }
.facts dd { margin: 0.15rem 0 0; font-family: var(--sm-font-mono); font-size: 1.15rem; font-variant-numeric: tabular-nums; }

.badge-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: 0.5rem; }
.badge-item { padding: 0.7rem 0.9rem; background: var(--sm-bg-raised); border: 1px solid var(--sm-border); display: flex; flex-direction: column; gap: 0.15rem; }
.badge-name { font-weight: 600; display: flex; align-items: center; gap: 0.45rem; }
.badge-desc { color: var(--sm-text-muted); font-size: 0.86rem; }

/* =============================== Footer ================================ */
.page-links {
  flex: 0 0 auto;
  display: flex; justify-content: center; gap: 1.5rem;
  padding: 2rem 1.5rem;
  border-top: 1px solid var(--sm-border);
  font-size: 0.9rem;
}
.page-links a { color: var(--sm-text-dim); }
.page-links a:hover { color: var(--sm-accent-hover); }

/* ================================ Appeals =============================== */
/*
  The appeals flow. Deliberately plainer than the leaderboard: somebody reading
  these pages has just been removed from a community and is looking for one
  fact, so the pages are narrow, the type is a little larger and there is
  nothing on them that is not part of the answer.
*/
.panel-form { max-width: 46rem; }
.panel-form h1 { margin-bottom: 1rem; font-size: clamp(1.6rem, 4vw, 2.1rem); }
.panel-form .stack { display: flex; flex-direction: column; gap: 0.9rem; align-items: flex-start; }
.panel-form .stack-item { display: flex; flex-direction: column; gap: 0.35rem; width: 100%; }

.form-label { font-weight: 600; font-size: 0.95rem; }
.field {
  width: 100%;
  padding: 0.65rem 0.8rem;
  background: var(--sm-bg-deep);
  border: 1px solid var(--sm-border-strong);
  color: var(--sm-text);
  font: inherit;
  border-radius: 0;
}
.field:focus-visible { outline: 1px solid var(--sm-accent); outline-offset: 1px; border-color: var(--sm-accent); }
textarea.field { resize: vertical; min-height: 6rem; }

.btn {
  display: inline-block;
  padding: 0.6rem 1.2rem;
  border: 1px solid var(--sm-border-strong);
  background: var(--sm-bg-raised);
  color: var(--sm-text);
  font: inherit;
  cursor: pointer;
}
.btn:hover { background: var(--sm-bg-hover); text-decoration: none; }
.btn-primary { background: var(--sm-accent); border-color: var(--sm-accent); color: #08131a; font-weight: 600; }
.btn-primary:hover { background: var(--sm-accent-hover); color: #08131a; }
/* Discord blurple, because it is Discord's brand on Discord's sign-in. */
.btn-discord { background: #5865f2; border-color: #5865f2; color: #fff; font-weight: 600; }
.btn-discord:hover { background: #4752c4; color: #fff; }
.btn-quiet { padding: 0.35rem 0.75rem; font-size: 0.86rem; color: var(--sm-text-muted); }

.hint { color: var(--sm-text-dim); font-size: 0.86rem; margin: 0; }
.mt { margin-top: 1.25rem; }
.notice-ok { border-color: rgba(59, 165, 92, 0.45); color: #7fd3a0; }

.appeal-crumb { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 1.25rem; color: var(--sm-text-muted); }
.appeal-crumb img { object-fit: cover; }

.whoami { display: flex; align-items: center; gap: 0.5rem; color: var(--sm-text-dim); font-size: 0.88rem; margin: 0 0 1.25rem; }
.whoami img { object-fit: cover; }

.reason-block {
  margin: 1rem 0;
  padding: 0.85rem 1rem;
  background: var(--sm-bg-raised);
  border: 1px solid var(--sm-border);
  border-left: 3px solid var(--sm-accent);
  white-space: pre-wrap;
}

.appeal-card { margin: 1.25rem 0; padding: 1rem 1.1rem; background: var(--sm-bg-raised); border: 1px solid var(--sm-border); }
.appeal-card header { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.5rem; }
.appeal-card p { margin: 0.35rem 0; }

.state { font-weight: 600; }
.state-ok { color: #7fd3a0; }
.state-part { color: var(--sm-warning); }
.state-no { color: #f08a8c; }
.state-wait { color: var(--sm-accent-hover); }
.state-off { color: var(--sm-text-dim); }

/* ================== Community hub, and the achievements ================ */
/* Both reuse the directory row's shape on purpose: a list of things to open,
   with the name on the left and a number on the right, is the same object
   whether the things are communities, pages or badges. */

/* The guild name in a hero doubles as the way back to its hub, so it is a link
   that does not look like one until it is hovered. */
.guild-home { color: inherit; text-decoration: none; }
.guild-home:hover { text-decoration: underline; }

.hub-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.hub-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: baseline; gap: 0.85rem;
  padding: 0.9rem 1.1rem;
  background: var(--sm-bg-raised);
  border: 1px solid var(--sm-border);
  color: var(--sm-text);
}
.hub-card:hover { background: var(--sm-bg-hover); border-color: var(--sm-border-strong); text-decoration: none; }
.hub-title { font-weight: 600; }
.hub-note { color: var(--sm-text-dim); font-size: 0.86rem; font-variant-numeric: tabular-nums; }

.ach-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.ach-item {
  display: grid;
  grid-template-columns: 2rem minmax(0, 1fr) auto;
  align-items: center; gap: 0.85rem;
  padding: 0.8rem 1.1rem;
  background: var(--sm-bg-raised);
  border: 1px solid var(--sm-border);
}
.ach-icon { font-size: 1.25rem; line-height: 1; text-align: center; }
.ach-body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.ach-name { font-weight: 600; }
.ach-desc { color: var(--sm-text-muted); font-size: 0.86rem; }
/* Tabular figures so the counts line up down the right-hand edge, which is what
   makes the list readable as a rarity ranking rather than as a set of rows. */
.ach-count {
  color: var(--sm-text-dim); font-size: 0.86rem;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
@media (max-width: 480px) { .ach-count-unit { display: none; } }

/* ========================= Signed-in account strip ====================== */
/* One line above everything, present only where sign-in means something —
   see accountStrip() in pages.js. Deliberately small: this site is public and
   read-only, and an account bar the size of a nav would suggest otherwise. */
.account-strip {
  flex: 0 0 auto;
  display: flex; align-items: center; justify-content: flex-end; gap: 0.5rem;
  padding: 0.5rem 1.5rem;
  border-bottom: 1px solid var(--sm-border);
  font-size: 0.82rem;
  color: var(--sm-text-dim);
}
.account-avatar { width: 22px; height: 22px; object-fit: cover; background: var(--sm-bg-deep); }
.account-name { color: var(--sm-text-muted); }
.account-link { color: var(--sm-accent-hover); text-decoration: none; }
.account-link:hover { text-decoration: underline; }

/* ========================= Light / dark toggle ==========================
   A link, because there is no JavaScript on this site. It sits top-right of
   every rendered page, above the account strip where there is one. */
.theme-bar {
  flex: 0 0 auto;
  display: flex; justify-content: flex-end;
  padding: 0.5rem 1.5rem 0;
}
.theme-toggle {
  display: inline-flex;
  border: 1px solid var(--sm-border-strong);
  color: var(--sm-text-dim);
  line-height: 0;
}
.theme-toggle:hover { border-color: var(--sm-accent); text-decoration: none; }
.theme-opt { padding: 0.25rem 0.5rem; display: grid; place-items: center; }
.theme-opt.is-on { background: var(--sm-accent); color: var(--sm-accent-fg); }

/* ====================== The empty / coming-soon page =====================
   One page for a slug nobody registered, a community that has not switched
   publishing on, and a member page that does not exist — see notFoundPage.
   It reads as a placeholder rather than an error, because most people who
   land here followed a community's own link. */
.panel-empty { padding-top: 4rem; }
.panel-empty h1 { margin-bottom: 0.6rem; }
.panel-empty p { margin: 0 0 0.5rem; }
.empty-mark {
  display: grid; place-items: center;
  width: 62px; height: 62px; margin: 0 auto 1.25rem;
  border: 2px solid var(--sm-accent);
  color: var(--sm-accent);
}

/* ============================ The appeals flow ==========================
   Same tokens as the portal and deliberately calmer with them. Somebody
   reading these has just been removed from a community: narrow column, no
   banner, and colour only where it carries meaning — what a decision was, or
   which refusal this is. See the note at the top of appealsPages.js. */

/* The four steps. Short, but nobody arriving here knows that. */
.step-rail { list-style: none; margin: 0 0 2rem; padding: 0; display: flex; gap: 0.4rem; }
.step { flex: 1 1 0; border-top: 3px solid var(--sm-border); padding-top: 0.5rem; }
.step-n {
  font-family: var(--sm-font-mono); font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.1em; color: var(--sm-text-faint);
}
.step-label { font-size: 0.82rem; color: var(--sm-text-dim); }
.step.is-done { border-top-color: var(--sm-accent); }
.step.is-here { border-top-color: var(--sm-accent); }
.step.is-here .step-n { color: var(--sm-accent); }
.step.is-here .step-label { color: var(--sm-text); }
@media (max-width: 520px) { .step-label { font-size: 0.74rem; } }

/* The bar every screen after the first one carries. */
.appeals-bar {
  display: flex; align-items: center; gap: 0.8rem;
  padding: 0.85rem 1.5rem;
  border-bottom: 1px solid var(--sm-border);
  background: var(--sm-bg-deep);
  flex-wrap: wrap;
}
.appeals-bar .sep { color: var(--sm-text-faint); }
.appeals-bar .where { font-weight: 700; letter-spacing: -0.01em; }
.appeals-bar .spacer { flex: 1 1 auto; }
/* The guild's OWN icon. The initial tile is only the fallback for a server
   that has none — see safeAvatar in pages.js. */
.guild-mark { width: 30px; height: 30px; object-fit: cover; display: block; background: var(--sm-bg-base); }
.guild-mark-fallback {
  display: grid; place-items: center;
  font-weight: 800; font-size: 0.78rem;
  color: var(--sm-text-dim); border: 1px solid var(--sm-border-strong);
}

/* What is being appealed. No case number: it is an internal reference the
   appellant has no use for. */
.fact-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.5rem; }
.fact-grid > div { background: var(--sm-bg-base); border: 1px solid var(--sm-border); padding: 0.6rem 0.75rem; }
.fact-k {
  font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--sm-text-dim); font-weight: 600;
}
.fact-v { font-family: var(--sm-font-mono); font-size: 1rem; font-weight: 700; margin-top: 0.1rem; }
@media (max-width: 520px) { .fact-grid { grid-template-columns: 1fr 1fr; } }

.sanction-panel { background: var(--sm-bg-raised); border: 1px solid var(--sm-border); border-left: 4px solid var(--sm-rose); padding: 1.15rem 1.3rem; }
.sanction-head { font-size: 0.76rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--sm-text-dim); margin-bottom: 0.8rem; }

/* Three promises, as ticks rather than a paragraph nobody reads. */
.checklist { display: flex; flex-direction: column; gap: 0.6rem; margin: 0; padding: 0; list-style: none; }
.checklist li { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.65rem; align-items: start; }
.checklist svg { margin-top: 0.25rem; }
.checklist span { color: var(--sm-text-muted); font-size: 0.9rem; }

/* The flow ending well: nothing to appeal. */
.panel-ok { background: var(--sm-bg-raised); border: 1px solid rgba(111,174,116,0.45); border-top: 4px solid var(--sm-success); padding: 1.8rem; }
.panel-ok h1 { font-size: 1.7rem; color: var(--sm-success); }
.ok-mark {
  width: 46px; height: 46px; flex: 0 0 auto;
  display: grid; place-items: center;
  background: rgba(111,174,116,0.14); border: 1px solid rgba(111,174,116,0.45);
  color: var(--sm-success);
}
.why-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.55rem; }
.why-list li { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.65rem; align-items: start; color: var(--sm-text-muted); font-size: 0.9rem; }
.why-dot { width: 5px; height: 5px; background: var(--sm-text-faint); margin-top: 0.6rem; }

/* A refusal that names itself. */
.refusal { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.75rem; align-items: start; padding: 0.7rem 0.95rem; background: var(--sm-bg-raised); border: 1px solid var(--sm-border); border-left: 3px solid var(--sm-warning); }
.refusal svg { margin-top: 0.2rem; }
.refusal-why { display: block; color: var(--sm-text-dim); font-size: 0.8rem; margin-top: 0.1rem; }

/* One centred line. Privacy and Terms were competing with the only thing on
   the page that matters. */
.appeals-footer { flex: 0 0 auto; display: flex; justify-content: center; padding: 1.4rem 1.5rem; border-top: 1px solid var(--sm-border); font-size: 0.88rem; }
.appeals-footer a { color: var(--sm-text-dim); }
.appeals-footer strong { color: var(--sm-accent); font-weight: 600; }

/* ============================ Community banner ==========================
   The community's own image where it set one, and a wash of the accent where
   it did not — a guild that has never opened Settings › Portal still gets a
   header with a shape rather than a bare heading. */
.banner {
  position: relative;
  min-height: 240px;
  display: flex; align-items: flex-end;
  border-bottom: 1px solid var(--sm-border);
  background:
    linear-gradient(115deg, var(--sm-accent) 0%, var(--sm-bg-deep) 62%, var(--sm-bg-base) 100%);
  overflow: hidden;
}
.banner-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Always over the image, so the name stays readable on whatever was uploaded —
   a bright banner must not be able to make a community's own title vanish. */
.banner-wash {
  position: absolute; inset: 0;
  background: linear-gradient(to top,
    var(--sm-bg-base) 0%,
    color-mix(in srgb, var(--sm-bg-base) 72%, transparent) 46%,
    transparent 100%);
}
.banner-inner {
  position: relative;
  width: 100%; max-width: 940px; margin: 0 auto;
  padding: 0 1.5rem 1.4rem;
  display: flex; align-items: flex-end; gap: 1.1rem;
}
.banner .guild-icon {
  width: 96px; height: 96px; flex: 0 0 auto;
  border: 2px solid var(--sm-accent);
  background: var(--sm-bg-deep);
  font-size: 2.2rem;
}
.banner-text { padding-bottom: 0.35rem; min-width: 0; }
.banner-text h1 { font-size: clamp(2rem, 6vw, 3rem); }
.banner-text .lede { margin: 0.15rem 0 0; color: var(--sm-text-muted); }
@media (max-width: 560px) {
  .banner { min-height: 190px; }
  .banner .guild-icon { width: 68px; height: 68px; font-size: 1.6rem; }
}

.intro {
  margin: 0 0 1.9rem;
  font-size: 1.15rem; line-height: 1.55;
  color: var(--sm-text); max-width: 62ch;
}

/* ============================== Hub cards ===============================
   One per page the community actually publishes — sections decides, so there
   is never a card onto a 404. */
.hub-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1rem;
}
.hub-card {
  display: flex; flex-direction: column; height: 100%;
  padding: 1.3rem 1.25rem 1.15rem;
  background: var(--sm-bg-raised);
  border: 1px solid var(--sm-border);
  border-top: 4px solid var(--hue, var(--sm-accent));
  color: var(--sm-text);
}
.hub-card:hover { background: var(--sm-bg-hover); border-color: var(--sm-border-strong); border-top-color: var(--hue, var(--sm-accent)); text-decoration: none; }
.hub-card.hue-amber { --hue: var(--sm-amber); }
.hub-card.hue-teal { --hue: var(--sm-teal); }
.hub-card.hue-rose { --hue: var(--sm-rose); }
.hub-card.hue-violet { --hue: var(--sm-violet); }
.hub-icon { color: var(--hue, var(--sm-accent)); }
.hub-title { font-size: 1.3rem; font-weight: 700; letter-spacing: -0.02em; margin-top: 0.85rem; }
.hub-say { color: var(--sm-text-muted); font-size: 0.92rem; margin-top: 0.3rem; flex: 1 1 auto; }
.hub-go {
  font-family: var(--sm-font-mono); font-size: 0.84rem; font-weight: 500;
  color: var(--hue, var(--sm-accent)); margin-top: 0.9rem;
}

/* ======================= Sub-page bar (portal) ==========================
   Server icon left, page name beside it — the same shape the appeals flow
   uses, so the two halves of the site read as one product. */
.page-bar {
  display: flex; align-items: center; gap: 0.85rem;
  padding: 0.85rem 1.5rem;
  border-bottom: 1px solid var(--sm-border);
  background: var(--sm-bg-deep);
}
.page-bar-mark { display: block; line-height: 0; }
.page-bar .guild-mark { width: 38px; height: 38px; border: 1px solid var(--sm-border-strong); }
.page-bar-name { color: var(--sm-text-muted); font-weight: 600; }
.page-bar-name:hover { color: var(--sm-accent); }
.page-bar .sep { color: var(--sm-text-faint); }
.page-bar .where { font-weight: 700; letter-spacing: -0.01em; }

/* ============================== The podium ==============================
   The first three, on the first page only. */
.podium {
  list-style: none; margin: 0 0 1.4rem; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.75rem;
}
.podium-card {
  display: flex; align-items: center; gap: 0.9rem; height: 100%;
  padding: 1.1rem;
  background: var(--sm-bg-raised);
  border: 1px solid var(--sm-border);
  border-top: 4px solid var(--medal, var(--sm-text-dim));
  color: var(--sm-text);
}
a.podium-card:hover { background: var(--sm-bg-hover); border-color: var(--sm-border-strong); border-top-color: var(--medal); text-decoration: none; }
.podium-slot.rank-1 .podium-card { --medal: var(--sm-gold); }
.podium-slot.rank-2 .podium-card { --medal: var(--sm-silver); }
.podium-slot.rank-3 .podium-card { --medal: var(--sm-bronze); }
.podium-avatar {
  width: 56px; height: 56px; flex: 0 0 auto; object-fit: cover;
  background: var(--sm-bg-base); border: 1px solid var(--sm-border);
  font-size: 1.3rem;
}
.podium-body { min-width: 0; display: flex; flex-direction: column; }
.podium-place {
  font-family: var(--sm-font-mono); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.12em; color: var(--medal);
}
.podium-name { font-weight: 700; font-size: 1.05rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.podium-xp { font-family: var(--sm-font-mono); font-size: 0.82rem; color: var(--sm-text-muted); }
@media (max-width: 640px) {
  .podium { grid-template-columns: 1fr; gap: 0.4rem; }
  .podium-avatar { width: 44px; height: 44px; }
}

/* =========================== Achievement rows ===========================
   The bar is drawn against the COMMONEST badge, not against the member count:
   the question a visitor has is "how unusual is this", not "what fraction of
   the server". Still a count and never a holder. */
.ach-item {
  display: grid; grid-template-columns: 52px minmax(0, 1fr);
  gap: 0.9rem; align-items: start;
  padding: 0.95rem 1.05rem;
  background: var(--sm-bg-raised);
  border: 1px solid var(--sm-border);
  border-top: 3px solid var(--hue, var(--sm-accent));
}
.ach-item.hue-amber { --hue: var(--sm-amber); }
.ach-item.hue-teal { --hue: var(--sm-teal); }
.ach-item.hue-violet { --hue: var(--sm-violet); }
.ach-item.hue-rose { --hue: var(--sm-rose); }
.ach-icon {
  width: 52px; height: 52px; display: grid; place-items: center;
  background: var(--sm-bg-base); border: 1px solid var(--sm-border);
  font-size: 1.5rem;
}
.ach-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.6rem; }
.ach-name { font-weight: 700; font-size: 1.02rem; }
.ach-count {
  font-family: var(--sm-font-mono); font-size: 0.82rem; font-weight: 700;
  color: var(--hue, var(--sm-accent)); white-space: nowrap;
}
.ach-bar {
  display: block; height: 5px; margin-top: 0.45rem;
  background: var(--sm-bg-base); border: 1px solid var(--sm-border);
}
.ach-bar-fill { display: block; height: 100%; background: var(--hue, var(--sm-accent)); }
.ach-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); gap: 0.6rem; }
