/*
 * Stash Network's look: krew-site's, in Stash Network's colours and images.
 * One column over the Discord banner's night-vision still, blurred and
 * dimmed; a header card with the nav; rounded, dashed cards; Geist Pixel
 * titles over small lowercase mono; dithered rules and buttons; a red glow
 * under the footer. BRAND.md, at the repo's root, is the guide, and
 * ../krew-site/site/static/app.css the look's source. Plain CSS, loaded
 * after the app's own stylesheets and outside their layers, so these rules
 * win over its utility classes too.
 *
 * Every colour, face and size is a custom property under "Tokens", the
 * only place with hex values. The site is always dark, in the Discord
 * icon's palette, with Stash red where krew-site has its purple. The app's
 * own theme variables (--background, --primary…) are set from the tokens.
 *
 * The page's blocks get their classes from {% .name %} annotations in
 * pages/index.md (.join, .card, .server, .faq…), and its staff list from
 * the staff-info module's {% staff %} (.staff-*). The rules and features
 * lists are styled by their headings (h2#rules + ol, h2#features + ul),
 * since a list can't take a class. The header and footer are each
 * module's reserved/header.md and reserved/footer.md, the same in pages/,
 * player-statistics/, forms/, accounts/, store/ and tournament/ (the admin
 * pages' header links them instead, and their footer is the same). The
 * stats pages' tags (.leaderboard, .server-list, .player-stats, .my-stats)
 * are under "Stats", the forms and account pages under "Forms", the store's
 * tiers, cart and perks tables under "Store", the tournaments' lists,
 * brackets and signups (.tournament-*), the home page's list too, under
 * "Tournaments", and the admin pages' tags (.admin-*) under "Admin".
 * Markers (▸ → ↗ ←) are pseudo-elements with empty alt text, so screen
 * readers skip them.
 */

@font-face {
  font-family: "Geist Pixel";
  src: url(/static/fonts/GeistPixel-Square.woff2) format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* --- Tokens (BRAND.md) ---------------------------------------------------- */

/* html:root rather than :root, so the tokens win over the app's own
   whatever order its stylesheets load in. */
html:root {
  color-scheme: dark;

  /* Colour: the Discord icon's palette (Void, Soot, Ash, Fog, Dust,
     Cinder) and Stash red. */
  --bg: #0b0908;
  --surface: #151010;
  --fg: #ece6e2;
  --fg-muted: #a89591;
  --fg-faint: #6e5d59;
  --line: #2f2422;
  --accent: #ff2a2a;
  --accent-ink: #ff2a2a;
  --on-accent: #0b0b0c;
  --ok: #3ddc84;
  --warn: #ffc233;
  --error: #ff4d5e;

  /* The join buttons' fills, the one place colour other than red comes
     in: Discord in its own, the staff application in the server's gold
     and the ban appeal in violet. Text on them is --on-discord or
     --on-accent. */
  --discord: #5865f2;
  --on-discord: #ffffff;
  --gold: #ffd700;
  --violet: #b48cf5;

  /* A dashed edge pointed at, and a row pointed at. */
  --line-hover: color-mix(in srgb, var(--fg) 25%, transparent);
  --accent-wash: color-mix(in srgb, var(--accent) 6%, transparent);

  /* Type: Geist Pixel for titles, Geist for the lead, and the system's
     mono for everything else, as krew-site has them. Geist is the CMS's
     own --font-sans, which it sets on <html> and every page preloads, so
     it isn't set here: a copy of our own would be a second download. */
  --font-pixel: "Geist Pixel", ui-monospace, monospace;
  --font-mono:
    ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
    "Courier New", monospace;
  --text-caption: 11px;
  --text-label: 12px;
  --text-body: 14px;
  --text-lead: 16px;
  --text-h2: 18px;
  --text-h1: 36px;

  /* Space: multiples of 4px. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;
  --space-8: 32px;
  --space-12: 48px;
  --space-16: 64px;
  --space-20: 80px;

  /* The column every page, header and footer share. */
  --column: 848px;

  /* Corners: cards, then pills and buttons. */
  --round: 8px;
  --round-small: 6px;

  /* The banner behind every page, and what dims it: enough that red text
     keeps 4.5:1 where the blurred photo is brightest. */
  --photo: url(/static/backgrounds/stash-still.webp);
  --veil: color-mix(in srgb, var(--bg) 65%, transparent);

  /* A card: Soot, a little see-through, over the photo. */
  --card: color-mix(in oklab, var(--surface) 55%, transparent);

  /* Dither: a checkerboard of currentColor, two squares to a tile, set
     with a background-size; as a mask, every other square shows. */
  --dither: repeating-conic-gradient(currentColor 0% 25%, transparent 0% 50%);
  --dither-mask: repeating-conic-gradient(black 0% 25%, transparent 0% 50%);

  /* The app's theme (src/app/globals.css), from the tokens. --accent is
     the token's own name too. */
  --background: var(--bg);
  --foreground: var(--fg);
  --card-foreground: var(--fg);
  --popover: var(--surface);
  --popover-foreground: var(--fg);
  --primary: var(--accent);
  --primary-foreground: var(--on-accent);
  --secondary: var(--surface);
  --secondary-foreground: var(--fg);
  --muted: var(--surface);
  --muted-foreground: var(--fg-muted);
  --accent-foreground: var(--on-accent);
  --destructive: var(--error);
  --border: var(--line);
  --input: var(--line);
  --ring: var(--accent-ink);
  --radius: var(--round);
}

/* --- The frame ------------------------------------------------------------ */

html {
  background-color: var(--bg);
}

body {
  background: transparent;
  color: var(--fg-muted);
  font-family: var(--font-mono);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

code,
kbd,
pre {
  font-family: var(--font-mono);
}

::selection {
  background: var(--accent);
  color: var(--on-accent);
}

/* The Discord banner's still behind everything, blurred and dimmed. */
body::before,
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -10;
  pointer-events: none;
}

body::before {
  background: var(--photo) center / cover no-repeat;
  filter: blur(4px);
  transform: scale(1.06);
}

body::after {
  background: var(--veil);
}

/* Dithered strips across the top and bottom of the page, and after each
   section's heading, fading at the sides. */
.site::before,
.site::after,
.page-content h2::after {
  content: "";
  color: var(--line);
  background-image: var(--dither);
  background-size: 6px 6px;
  mask-image: linear-gradient(90deg, transparent, black 18% 82%, transparent);
}

.site::before,
.site::after {
  flex: none;
  height: var(--space-2);
}

.site-main {
  max-width: calc(var(--column) + 2 * var(--space-6));
  padding: var(--space-16) var(--space-6) 0;
}

/* Keyboard focus, everywhere: a red ring past a dark one, so one keeps
   3:1 against whatever is behind them, photo or card. */
.site :is(a, button, summary, input, textarea, select):focus-visible {
  outline: 2px solid var(--accent-ink);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--bg);
}

/* --- Header --------------------------------------------------------------- */

/* Every page's header, the width of the footer: two boxes side by side.
   The nav's card holds the mark and name back home, then
   {% render "nav" /%}'s links as dashed pills, the current one red
   (here(), in static/app.js); the account link is a box of its own beside
   it. */
.site-header {
  --header-bg: color-mix(in oklab, var(--surface) 70%, transparent);
  width: min(calc(100% - 2 * var(--space-6)), var(--column));
  margin: var(--space-6) auto 0;
  border: 0;
}

/* The name and nav share a row, and the nav's card is drawn behind both
   by ::before, which takes their two columns; the account link takes the
   third. On phones the nav goes under the name, in the same card. */
.site-header-inner {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: 0 var(--space-2);
  isolation: isolate;
  min-height: 0;
  max-width: none;
  padding: 0;
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
}

.site-header-inner::before {
  content: "";
  grid-column: 1 / 3;
  grid-row: 1;
  align-self: stretch;
  z-index: -1;
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--header-bg);
}

.site-header .nav-here {
  display: none;
}

.site-header .site-brand {
  grid-column: 1;
  grid-row: 1;
  padding: var(--space-2) 0 var(--space-2) var(--space-4);
  font-family: var(--font-pixel);
  font-size: var(--text-body);
  line-height: 1.43;
  font-weight: 400;
  text-transform: none;
}

.site-header .site-brand a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--accent-ink);
  text-decoration: none;
  white-space: nowrap;
}

/* The mark before the name, a rounded square like the pills: a 96px copy
   of img/logo.png. Drawn here, inside the link, since Markdoc won't put an
   image in a link; screen readers hear only the name. */
.site-header .site-brand a::before {
  content: "";
  flex: none;
  width: var(--space-6);
  height: var(--space-6);
  border-radius: var(--round-small);
  background: url(/static/img/logo-96.png) center / cover;
}

.site-nav {
  grid-column: 2;
  grid-row: 1;
  gap: 6px;
  padding: var(--space-2) var(--space-3) var(--space-2) 0;
  font-size: inherit;
}

.site-nav a {
  padding: var(--space-1) 6px;
  border: 1px dashed var(--line);
  border-radius: var(--round-small);
  color: var(--fg-muted);
  text-decoration: none;
  transition:
    color 0.15s,
    border-color 0.15s;
}

.site-nav a:hover {
  border-color: var(--line-hover);
  color: var(--fg);
}

.site-nav a[aria-current] {
  color: var(--accent-ink);
}

/* The account link: "my account", or "sign in" for visitors who aren't
   signed in. A box of its own, as tall as the nav's card, with the same
   dashed edge. */
.site-account {
  grid-column: 3;
  grid-row: 1;
  align-self: stretch;
  display: flex;
}

.site-account a {
  display: flex;
  align-items: center;
  padding: 0 var(--space-3);
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--header-bg);
  color: var(--fg);
  text-decoration: none;
  white-space: nowrap;
  transition: border-color 0.15s;
}

.site-account a:hover {
  border-color: var(--line-hover);
}

@media (width < 40rem) {
  .site-header-inner {
    grid-template-columns: 1fr auto;
  }

  .site-header-inner::before {
    grid-column: 1;
    grid-row: 1 / 3;
  }

  .site-header .site-brand {
    padding: var(--space-2) var(--space-3) 0 var(--space-4);
  }

  .site-nav {
    grid-column: 1;
    grid-row: 2;
    padding: var(--space-2) var(--space-3) var(--space-2) var(--space-4);
  }

  .site-account {
    grid-column: 2;
    grid-row: 1 / 3;
  }
}

.site-header + .site-main {
  padding-top: var(--space-12);
}

/* --- Footer --------------------------------------------------------------- */

/* The name and the year, then the mark's credit, over a red glow rising
   from the bottom, dithered in 3px squares. It fades out under the text,
   so the red name keeps 4.5:1 over it. */
.site-footer {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  width: min(calc(100% - 2 * var(--space-6)), var(--column));
  margin: var(--space-20) auto var(--space-16);
  padding: var(--space-6) 0 56px;
  border-top: 1px solid var(--line);
  border-bottom: 0;
}

.site-footer::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--accent) 40%, transparent),
    transparent 75%
  );
  mask: var(--dither-mask) 0 0 / 6px 6px;
}

.site-footer-inner {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  max-width: none;
  padding: 0 var(--space-6);
  font-size: var(--text-caption);
  line-height: 1.625;
  color: var(--fg-muted);
}

.site-footer-inner > * + * {
  margin-top: 0;
}

.site-footer .footer-brand {
  font-family: var(--font-pixel);
  font-size: var(--text-body);
  line-height: 1.43;
  color: var(--accent-ink);
}

.site-footer .footer-credit {
  grid-column: 1 / -1;
}

.site-footer .footer-credit a {
  color: var(--fg);
  text-decoration: underline dashed;
  text-underline-offset: 4px;
}

/* PayNow's terms, which every store page has to link, as PayNow takes the
   payment: a line of small muted print right under the page's content, in
   its column (store/reserved/app.md). It, not the page, takes the height a
   short page leaves, so it stays under the content rather than sinking to
   the footer. Its links are ink, underlined like the footer's. */
.site .site-main:has(+ .store-legal) {
  flex: none;
}

.site .store-legal {
  flex: 1 0 auto;
  width: min(calc(100% - 2 * var(--space-6)), var(--column));
  margin: var(--space-8) auto 0;
  font-size: var(--text-caption);
  line-height: 1.625;
  color: var(--fg-muted);
  text-transform: lowercase;
}

.site .store-legal a {
  color: var(--fg);
  text-decoration: underline dashed;
  text-underline-offset: 4px;
}

.site .store-legal + .site-footer {
  margin-top: var(--space-8);
}

/* --- Text ----------------------------------------------------------------- */

.page-content {
  font-size: var(--text-body);
  line-height: 1.625;
}

.page-content > * + * {
  margin-top: var(--space-4);
}

.page-content h1,
.form-responses h1 {
  font-family: var(--font-pixel);
  font-size: var(--text-h1);
  line-height: 1.1;
  font-weight: 400;
  letter-spacing: normal;
  color: var(--accent-ink);
  text-wrap: balance;
}

@media (width >= 40rem) {
  .page-content h1,
  .form-responses h1 {
    font-size: 48px;
    line-height: 1;
  }
}

/* Titles in lowercase, like krew-site's, but for names: the home page's
   (class: home) and the stats pages titled with a server's or a player's
   (class: name-title in their frontmatter). */
.page-content:not(.home, .name-title) > h1,
.form-responses h1 {
  text-transform: lowercase;
}

/* Section headings, followed by a dithered rule to the edge: a page's, and
   those splitting a form into parts. */
.page-content > h2,
.page-content .form-questions > h2 {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-20);
  font-family: var(--font-pixel);
  font-size: var(--text-h2);
  line-height: 1.55;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: lowercase;
  color: var(--fg);
  scroll-margin-top: var(--space-6);
}

.page-content h2::after {
  flex: 1;
  height: 6px;
}

/* The brand name, wherever a page writes **{% $appName %}**. */
.page-content strong {
  font-weight: inherit;
  color: var(--accent-ink);
  text-transform: none;
}

/* _Emphasis_: ink, not italic. */
.page-content em {
  font-style: normal;
  color: var(--fg);
}

.page-content a:not([data-slot="button"]) {
  font-weight: inherit;
  color: var(--fg);
  text-decoration: underline dashed;
  text-underline-offset: 4px;
  transition:
    color 0.15s,
    text-decoration-color 0.15s;
}

.page-content a:not([data-slot="button"]):hover {
  color: var(--fg);
  text-decoration-color: var(--fg);
}

/* Small mono captions: cards, server rows, form rows and the features. */
.page-content
  :is(.card, .server, .form-row, .faq, .chart-label, .player-total, h2#features + ul) {
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
}

.page-content :is(.card, .server, .form-row, .faq) p {
  max-width: 65ch;
}

/* Inline code as a dashed pill: "daily", the server's address. */
.page-content :is(.chart-label, .server-join) code {
  padding: var(--space-1) 6px;
  border: 1px dashed var(--line);
  border-radius: var(--round-small);
  background: none;
  font: inherit;
  white-space: nowrap;
}

.page-content code {
  font-size: inherit;
  color: var(--fg);
  background: none;
}

/* --- Home ----------------------------------------------------------------- */

.page-content .lead {
  max-width: 65ch;
  font-family: var(--font-sans);
  font-size: var(--text-lead);
  line-height: 1.625;
  text-wrap: balance;
}

.page-content .chart-label {
  display: flex;
  align-items: center;
  gap: 6px;
}

.page-content > .chart-label + .player-chart {
  margin-top: 6px;
}

/* The game-analytics module's {% player-chart %}: a dithered red area
   under a glowing line, with the caption under the plot. */
.page-content .player-chart {
  --player-chart-color: var(--accent);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
}

.page-content .player-chart > figcaption {
  order: 1;
  font: inherit;
  color: var(--fg-muted);
}

.page-content .player-chart-range {
  font-weight: inherit;
}

.page-content .player-chart-body {
  font-size: inherit;
  line-height: inherit;
}

.page-content .player-chart-plot {
  height: 56px;
  border-bottom: 0;
  border-left: 0;
}

.page-content .player-chart-grid {
  stroke: transparent;
}

/* Every other pixel of the area, like the dither over the server art. */
.page-content .player-chart-fill {
  mask-image: var(--dither-mask);
  mask-size: 2px 2px;
}

.page-content .player-chart-area {
  fill-opacity: 0.45;
}

.page-content .player-chart-lines {
  filter: drop-shadow(
    0 0 6px color-mix(in srgb, var(--accent) 60%, transparent)
  );
}

.page-content .player-chart-tooltip {
  border: 1px dashed var(--line);
  border-radius: var(--round-small);
  background: var(--surface);
  color: var(--fg);
  text-transform: lowercase;
}

/* Under the chart, the player-statistics module's {% player-count %}
   (pages/index.md says how to bring it back). */
.page-content .player-total {
  color: var(--fg-muted);
}

.page-content > .player-chart + .player-total {
  margin-top: var(--space-2);
}

.page-content .player-total .player-count {
  color: var(--fg);
}

/* --- Join buttons --------------------------------------------------------- */

.page-content .join-buttons {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

/* Each button sets its fill, --join, its text, --join-text, and its
   --icon; without them, it's Stash red. Like krew-site's, a dither of
   --join-shade thickens toward the bottom: a shade moved away from the
   text's colour, so the label keeps the contrast it has on the fill. On
   hover the dither fills in and the button glows in its colour. Every
   form's main button and the store's ("subscribe", "check out at PayNow",
   confirming a move to another tier) are dressed the same in red, and
   signing in with Discord or Steam in ink (below); :where() keeps those as
   specific as .join alone, so each button's own colours still win. */
.page-content
  :is(
    .join,
    :where(
      form[data-provider] > [data-slot="button"],
      :is(.form, .account-form, .account-settings, .store-add, .store-checkout, .store-tier-switch, .tournament-signup, .tournament-check-in, .admin-panel) .bg-primary
    )
  ) {
  --join: var(--accent);
  --join-text: var(--on-accent);
  --join-shade: color-mix(in srgb, var(--accent) 75%, var(--fg));
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: auto;
  padding: var(--space-2) var(--space-4);
  border: 0;
  border-radius: var(--round-small);
  background: var(--join);
  box-shadow: inset 0 0 0 1px
    color-mix(in srgb, var(--join-shade) 50%, transparent);
  color: var(--join-text);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  line-height: 16px;
  font-weight: 400;
  text-transform: lowercase;
  text-decoration: none;
  white-space: nowrap;
  translate: none;
  transition:
    box-shadow 0.15s,
    opacity 0.15s;
}

.page-content
  :is(
    .join,
    :where(
      form[data-provider] > [data-slot="button"],
      :is(.form, .account-form, .account-settings, .store-add, .store-checkout, .store-tier-switch, .tournament-signup, .tournament-check-in, .admin-panel) .bg-primary
    )
  )::before {
  content: "";
  flex: none;
  width: 14px;
  height: 14px;
  background-color: currentColor;
  mask: var(--icon) center / contain no-repeat;
}

/* The dithered gradient behind the label. */
.page-content
  :is(
    .join,
    :where(
      form[data-provider] > [data-slot="button"],
      :is(.form, .account-form, .account-settings, .store-add, .store-checkout, .store-tier-switch, .tournament-signup, .tournament-check-in, .admin-panel) .bg-primary
    )
  )::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(transparent 10%, var(--join-shade));
  mask: var(--dither-mask) 0 0 / 4px 4px;
  opacity: 0.8;
  transition: opacity 0.15s;
}

.page-content
  :is(
    .join,
    :where(
      form[data-provider] > [data-slot="button"],
      :is(.form, .account-form, .account-settings, .store-add, .store-checkout, .store-tier-switch, .tournament-signup, .tournament-check-in, .admin-panel) .bg-primary
    )
  ):hover {
  background: var(--join);
  box-shadow:
    inset 0 0 0 1px var(--join-shade),
    0 0 18px color-mix(in srgb, var(--join) 35%, transparent);
}

.page-content
  :is(
    .join,
    :where(
      form[data-provider] > [data-slot="button"],
      :is(.form, .account-form, .account-settings, .store-add, .store-checkout, .store-tier-switch, .tournament-signup, .tournament-check-in, .admin-panel) .bg-primary
    )
  ):hover::after {
  opacity: 1;
}

.page-content
  :is(
    .join,
    :where(
      form[data-provider] > [data-slot="button"],
      :is(.form, .account-form, .account-settings, .store-add, .store-checkout, .store-tier-switch, .tournament-signup, .tournament-check-in, .admin-panel) .bg-primary
    )
  ):focus-visible {
  box-shadow: 0 0 0 4px var(--bg);
}

/* The join row, each in its own colour so it reads at a glance: Discord
   in its own, the store in Stash red (the default), the rules in ink, the
   server list in Stash red too, apart from the store's, the staff
   application in gold and the ban appeal in violet. Darker shades for
   light text, lighter ones for dark. Icons are Simple Icons' (Discord) and
   Lucide's, in static/icons/. */
.page-content .join.join-discord {
  --join: var(--discord);
  --join-text: var(--on-discord);
  --join-shade: color-mix(in srgb, var(--discord) 65%, var(--bg));
  --icon: url(/static/icons/discord.svg);
}

.page-content .join.join-store {
  --icon: url(/static/icons/heart-handshake.svg);
}

.page-content .join.join-rules {
  --join: var(--fg);
  --join-text: var(--bg);
  --join-shade: color-mix(in srgb, var(--fg) 80%, var(--fg-muted));
  --icon: url(/static/icons/scroll-text.svg);
}

.page-content .join.join-list {
  --icon: url(/static/icons/list.svg);
}

.page-content .join.join-staff {
  --join: var(--gold);
  --join-shade: color-mix(in srgb, var(--gold) 70%, var(--fg));
  --icon: url(/static/icons/at-sign.svg);
}

.page-content .join.join-bans {
  --join: var(--violet);
  --join-shade: color-mix(in srgb, var(--violet) 70%, var(--fg));
  --icon: url(/static/icons/gavel.svg);
}

/* Signing in with Discord or Steam (accounts/), in ink with its mark: apart
   from the red password button beside it. Each form's, account form's and
   the store's main button is red, without an icon. */
.page-content form[data-provider] > [data-slot="button"] {
  --join: var(--fg);
  --join-text: var(--bg);
  --join-shade: color-mix(in srgb, var(--fg) 80%, var(--fg-muted));
}

.page-content form[data-provider="steam"] > [data-slot="button"] {
  --icon: url(/static/icons/steam.svg);
}

.page-content form[data-provider="discord"] > [data-slot="button"] {
  --icon: url(/static/icons/discord.svg);
}

.page-content form[data-provider] > [data-slot="button"] svg {
  display: none;
}

.page-content :is(.form, .account-form, .account-settings, .store-add, .store-checkout, .store-tier-switch, .tournament-signup, .tournament-check-in, .admin-panel) .bg-primary::before {
  content: none;
}

/* --- Cards ---------------------------------------------------------------- */

.page-content :is(.callout.card, .columns.server, .columns.form-row, .details.faq) {
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
}

.page-content .callout.card {
  padding: 0;
}

.page-content .callout.card > p {
  padding: var(--space-3) var(--space-4) 0;
}

.page-content .callout.card > * + * {
  margin-top: 0;
}

/* Rows of the same kind sit closer together than sections. */
.page-content > :is(.server + .server, .form-row + .form-row, .faq + .faq) {
  margin-top: 6px;
}

/* --- Collapsible sections ------------------------------------------------- */

/* {% details %}: its summary after a ▸ that turns down when it's open. It
   opens and closes at once, without animating. */
.page-content .details {
  padding: 0;
  border: 0;
  border-radius: 0;
}

.page-content .details > summary {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  list-style: none;
  font-weight: 400;
  color: var(--fg-muted);
  cursor: pointer;
  transition: color 0.15s;
}

.page-content .details > summary::-webkit-details-marker {
  display: none;
}

.page-content .details > summary::before {
  content: "▸";
  content: "▸" / "";
}

.page-content .details[open] > summary::before {
  transform: rotate(90deg);
}

.page-content .details > summary:hover {
  color: var(--fg);
}

.page-content .details-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-3) 0 0;
}

.page-content .details-body > * + * {
  margin-top: 0;
}

/* "more" in the about card, and each FAQ: the whole card's width. */
.page-content :is(.callout.card, .faq) > summary,
.page-content .callout.card > .details > summary {
  padding: var(--space-3) var(--space-4);
}

.page-content .callout.card > .details > .details-body,
.page-content .faq > .details-body {
  padding: 0 var(--space-4) var(--space-3);
}

.page-content .faq > summary {
  font-family: var(--font-pixel);
  font-size: var(--text-body);
  line-height: 1.43;
}

.page-content .faq > summary:focus-visible,
.page-content .callout.card > .details > summary:focus-visible {
  outline-offset: -2px;
  box-shadow: inset 0 0 0 4px var(--bg);
}

/* --- Game server ---------------------------------------------------------- */

.page-content .columns.server {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
}

.page-content .server > .column > * + * {
  margin-top: 0;
}

.page-content .server-art {
  position: relative;
  flex: none;
  width: var(--space-8);
  height: var(--space-8);
  overflow: hidden;
  border-radius: var(--round-small);
}

.page-content .server-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 0;
  filter: grayscale(1) brightness(0.9);
  transition: filter 0.22s;
}

/* A fine dither over the art, lifted with the greyscale on hover. */
.page-content .server-art::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  color: var(--surface);
  background-image: var(--dither);
  background-size: 2px 2px;
  transition: opacity 0.22s;
}

@media (hover: hover) {
  .page-content .server:is(:hover, :focus-within) .server-art img {
    filter: none;
  }

  .page-content .server:is(:hover, :focus-within) .server-art::after {
    opacity: 0;
  }
}

.page-content .server[data-revealed] .server-art img {
  filter: none;
}

.page-content .server[data-revealed] .server-art::after {
  opacity: 0;
}

.page-content .server-info {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--space-1);
}

.page-content .server-name {
  font-size: var(--text-label);
  line-height: 1.33;
  color: var(--fg);
}

/* Status: a dot, then the state in words, its colour the state's. */
.page-content .server-status {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.page-content .server-status-dot {
  flex: none;
  width: var(--space-2);
  height: var(--space-2);
  border-radius: 9999px;
  background: var(--fg-faint);
}

.page-content .server-status[data-status="online"] .server-status-dot {
  background: var(--ok);
}

.page-content .server-status[data-status="unknown"] .server-status-dot {
  background: var(--warn);
}

.page-content .server-status[data-status="offline"] .server-status-dot {
  background: var(--error);
}

.page-content .server-join {
  flex: none;
}

/* The address beside the join link, with the stats link under it: both
   links in the second column, the same width. */
.page-content .server-join p {
  display: grid;
  grid-template-columns: auto auto;
  align-items: center;
  gap: 6px;
}

.page-content .server-join a + a {
  grid-column: 2;
}

.page-content .server-join a:not([data-slot="button"]) {
  padding: var(--space-1) 6px;
  text-align: center;
  border: 1px solid var(--line);
  border-radius: var(--round-small);
  color: var(--fg-muted);
  text-decoration: none;
  white-space: nowrap;
  transition:
    color 0.15s,
    border-color 0.15s;
}

.page-content .server-join a:not([data-slot="button"]):hover {
  border-color: var(--line-hover);
  color: var(--fg);
}

/* Their arrows: ↗ on "join", which leaves the site for Steam, and → on
   "stats", which stays on it. */
.page-content .server-join a:not([data-slot="button"])::after {
  content: " →";
  content: " →" / "";
}

.page-content .server-join a:not([data-slot="button"]):not([href^="/"])::after {
  content: " ↗";
  content: " ↗" / "";
}

/* On phones the address and links go under the server's details. */
@media (width < 40rem) {
  .page-content .columns.server {
    flex-wrap: wrap;
  }

  .page-content .server-info {
    flex-basis: calc(100% - 44px);
  }

  .page-content .server-join {
    margin-left: 44px;
  }
}

/* --- Features and rules --------------------------------------------------- */

/* The features: dashed pills that wrap, like the server's links. */
.page-content h2#features + ul {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0;
  list-style: none;
}

.page-content h2#features + ul > li {
  margin: 0;
  padding: var(--space-1) var(--space-2);
  border: 1px dashed var(--line);
  border-radius: var(--round-small);
  background: var(--card);
  color: var(--fg);
}

/* The rules: one card, a dashed row each, numbered; 5's parts as 05.1 and
   05.2. Each number sits in its row's padding, so the rule, emphasis and
   code and all, wraps as one paragraph. */
.page-content h2#rules + ol {
  padding: 0 var(--space-4);
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-label);
  line-height: 1.625;
  list-style: none;
  counter-reset: rule;
}

.page-content h2#rules + ol > li {
  position: relative;
  margin: 0;
  padding: var(--space-3) 0 var(--space-3) calc(2ch + var(--space-4));
  color: var(--fg);
  counter-increment: rule;
}

.page-content h2#rules + ol > li + li {
  border-top: 1px dashed var(--line);
}

.page-content h2#rules + ol li::before {
  content: counter(rule, decimal-leading-zero);
  content: counter(rule, decimal-leading-zero) / "";
  position: absolute;
  left: 0;
  color: var(--fg-muted);
}

.page-content h2#rules + ol ol {
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
  counter-reset: part;
}

.page-content h2#rules + ol ol > li {
  position: relative;
  margin: var(--space-1) 0 0;
  padding-left: calc(4ch + var(--space-4));
  color: var(--fg-muted);
  counter-increment: part;
}

.page-content h2#rules + ol ol > li::before {
  content: counter(rule, decimal-leading-zero) "." counter(part);
  content: counter(rule, decimal-leading-zero) "." counter(part) / "";
}

.page-content .rules-note {
  font-size: var(--text-label);
}

/* --- Staff ---------------------------------------------------------------- */

/* {% staff %}, the staff-info module's list on the home page: each
   person's Discord picture, username and role, from their roles on the
   Discord server. The CMS lays it out as a wrapping row; this gives it
   krew-site's look, on the photo rather than in a card. Each person is
   104px wide, room for a 15-character name on one line, so the columns
   line up and two-word roles wrap alike. Roles stay muted, not their
   Discord colours. */
.page-content .staff-list {
  gap: var(--space-3);
}

.page-content .staff-member {
  width: 104px;
  gap: var(--space-1);
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
  color: var(--fg);
}

.page-content .staff-avatar {
  border-radius: var(--round-small);
  background: var(--surface);
}

.page-content .staff-name {
  font-weight: inherit;
}

.page-content .staff-role {
  color: var(--fg-muted);
}

/* What it says while Discord can't be reached, or no one has a role. */
.page-content p.staff {
  font-size: var(--text-caption);
  text-transform: lowercase;
}

/* --- Stats ---------------------------------------------------------------- */

/* {% server-list %}: a card per server, like the home page's, with its
   game's art from its id. The whole card is its link. */
.page-content .server-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 0;
  list-style: none;
}

.page-content .server-list > li {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
  transition: border-color 0.15s;
}

.page-content .server-list > li[data-server="scpsl"] {
  --game: url(/static/games/scpsl.png);
}

.page-content .server-list > li::before {
  content: "";
  flex: none;
  width: var(--space-8);
  height: var(--space-8);
  border-radius: var(--round-small);
  color: var(--surface);
  background:
    var(--dither) 0 0 / 2px 2px,
    var(--game, none) center / cover,
    var(--surface);
  filter: grayscale(1) brightness(0.9);
  transition: filter 0.22s;
}

.page-content .server-list > li:is(:hover, :focus-within) {
  border-color: var(--line-hover);
}

.page-content .server-list > li:is(:hover, :focus-within)::before {
  background: var(--game, none) center / cover;
  filter: none;
}

.page-content .server-list a:not([data-slot="button"]) {
  flex: 1;
  font-size: var(--text-label);
  line-height: 1.33;
  text-decoration: none;
}

.page-content .server-list a:not([data-slot="button"])::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.page-content .server-list a:focus-visible {
  outline: none;
  box-shadow: none;
}

.page-content .server-list > li:has(a:focus-visible) {
  outline: 2px solid var(--accent-ink);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--bg);
}

.page-content .server-list-players {
  flex: none;
  padding: var(--space-1) 6px;
  border: 1px dashed var(--line);
  border-radius: var(--round-small);
  font-size: inherit;
  white-space: nowrap;
}

/* {% leaderboard %} and {% leaderboards %}: dashed cards with a pixel title
   and a plain table, the top player's place in red. .boards is a
   {% columns %} of them, two or more to a column. */
.page-content :is(.leaderboards, .columns.boards) {
  gap: var(--space-3);
}

.page-content .columns.boards > .column > * + * {
  margin-top: var(--space-3);
}

.page-content .leaderboard {
  overflow: hidden;
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
}

.page-content p.leaderboard,
.page-content .leaderboard-empty {
  padding: var(--space-3) var(--space-4);
}

.page-content .leaderboard-title {
  margin: 0;
  padding: var(--space-3) var(--space-4) var(--space-2);
  font-family: var(--font-pixel);
  font-size: var(--text-body);
  line-height: 1.43;
  font-weight: 400;
  color: var(--fg);
}

.page-content :is(.leaderboard, .player-stats) :is(th, td) {
  padding: var(--space-1) var(--space-2);
  border: 0;
  border-top: 1px dashed var(--line);
  background: none;
  font-weight: 400;
  vertical-align: baseline;
}

.page-content :is(.leaderboard, .player-stats) :is(th, td):first-child {
  padding-left: var(--space-4);
}

.page-content :is(.leaderboard, .player-stats) :is(th, td):last-child {
  padding-right: var(--space-4);
}

.page-content :is(.leaderboard, .player-stats) thead th {
  color: var(--fg-muted);
}

.page-content .leaderboard .leaderboard-rank {
  width: 48px;
}

.page-content .leaderboard-player {
  text-transform: none;
  overflow-wrap: anywhere;
}

.page-content .leaderboard-player a:not([data-slot="button"]) {
  text-decoration-color: transparent;
}

.page-content .leaderboard-player a:not([data-slot="button"]):hover {
  text-decoration-color: var(--fg);
}

.page-content .leaderboard td.leaderboard-value {
  color: var(--fg);
  white-space: nowrap;
}

.page-content .leaderboard tbody tr {
  transition: background-color 0.15s;
}

.page-content .leaderboard tbody tr:hover {
  background: var(--accent-wash);
}

.page-content .leaderboard tr[data-rank="1"] .leaderboard-rank {
  color: var(--accent-ink);
}

/* {% player-stats %}: the same plain table, as a card of its own. */
.page-content table.player-stats {
  border-collapse: separate;
  border-spacing: 0;
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
}

.page-content .player-stats thead th {
  border-top: 0;
  padding-top: var(--space-3);
}

.page-content .player-stats tbody tr:last-child > * {
  padding-bottom: var(--space-3);
}

.page-content .player-stats tbody th {
  color: var(--fg);
}

.page-content .player-stats td {
  text-align: right;
  white-space: nowrap;
  color: var(--fg);
}

.page-content .player-stats thead th:not(:first-child) {
  text-align: right;
}

.page-content .player-stats-rank {
  font-size: inherit;
}

/* {% my-stats %}: the signed-in visitor's own .player-stats table, and on
   the stats home page their page on the server under it. Without Steam
   linked, or with no stats yet, its one line is a card like an empty
   board's. */
.page-content .my-stats > p:only-child {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
}

.page-content :is(.my-stats > p:only-child, .my-stats-servers) {
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
}

/* A player's page: when they've played, under their name; and the notes
   under boards. */
.page-content :is(.player-seen, .player-note, .boards-more, .tournaments-more) {
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
}

.page-content > h1 + .player-seen {
  margin-top: var(--space-3);
}

/* "every leaderboard", under the home page's boards, and "every
   tournament", under its tournaments: → as they stay on the site. */
.page-content :is(.boards-more, .tournaments-more) a:not([data-slot="button"])::after {
  content: " →";
  content: " →" / "";
}

/* "← back" above the heading of every stats page but the home, to the page
   above it, and above each form. Plain ink text, with none of the button's
   own background or border, and a dashed underline on hover like the
   pages' links. */
.page-content .back {
  height: auto;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--fg);
  font: var(--text-label) / 16px var(--font-mono);
  text-transform: lowercase;
  text-decoration: underline dashed transparent;
  text-underline-offset: 4px;
  translate: none;
  transition: text-decoration-color 0.15s;
}

.page-content .back::before {
  content: "←";
  content: "←" / "";
}

.page-content .back:hover {
  background: none;
  color: var(--fg);
  text-decoration-color: var(--fg);
}

.page-content .back:focus-visible {
  outline-offset: 4px;
  box-shadow: 0 0 0 6px var(--bg);
}

/* --- Forms ---------------------------------------------------------------- */

/* The forms module's {% form %} (forms/), the accounts module's sign-in,
   sign-up and account forms (accounts/), and the admins' responses pages:
   each a dashed card like the home page's, in its small lowercase type.
   What people type keeps its case. Field edges are --fg-muted, 3:1 and
   more against the card. Their main buttons are dressed in "Join
   buttons". */
.page-content :is(.form, .account-form, .account-settings),
.form-responses {
  padding: var(--space-4);
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-label);
  line-height: 1.625;
  text-transform: lowercase;
}

.page-content .account-form {
  max-width: 28rem;
}

/* Bold text on them, such as the account page's name, stays ink. */
.page-content :is(.form, .account-form, .account-settings) strong {
  color: var(--fg);
}

/* A form's parts sit closer than a page's sections. */
.page-content .form-questions > h2 {
  margin-top: 40px;
}

.page-content .form-questions > h2:first-child {
  margin-top: 0;
}

.page-content .form-questions > ul {
  padding-left: var(--space-4);
  list-style: "– ";
}

/* The account page's sections, headed like the FAQ. */
.page-content .account-settings h2 {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
  font-family: var(--font-pixel);
  font-size: var(--text-body);
  line-height: 1.43;
  font-weight: 400;
  letter-spacing: normal;
  color: var(--fg);
}

/* Labels like the form rows' names, with a question's * in red after its
   last word; help, hints and errors like the cards' captions. */
.page-content :is(.form-label, .account-field label) {
  display: block;
  font-size: var(--text-label);
  line-height: 1.625;
  font-weight: 400;
  color: var(--fg);
}

.page-content .form-required {
  margin-left: var(--space-1);
  color: var(--accent-ink);
}

.page-content :is(.form, .account-form, .account-settings) :is(p, li),
.page-content :is(.form-option, .form-problem, .account-notice) {
  font-size: var(--text-label);
}

.page-content
  :is(.form-help, .form-error, .form-admin, .form-respondent, .account-field p, .account-field button) {
  font-size: var(--text-caption);
  line-height: 1.625;
}

.page-content .form-error {
  color: var(--error);
}

/* Text fields as dashed boxes, like the cards' links: the store's "how
   many" boxes too. */
.page-content
  :is(.form-question, .account-field, .store-add, .store-cart, .tournament-signup, .tournament-check-in, .admin-panel)
  :is(input:not([type="checkbox"], [type="radio"]), textarea, select) {
  border: 1px dashed var(--fg-muted);
  border-radius: var(--round-small);
  background: transparent;
  color: var(--fg);
  font-family: var(--font-mono);
  text-transform: none;
}

.page-content
  :is(.form-question, .account-field, .store-add, .store-cart, .tournament-signup, .tournament-check-in, .admin-panel)
  :is(input, textarea, select)::placeholder {
  color: var(--fg-muted);
}

.page-content
  :is(.form-question, .account-field, .store-add, .store-cart, .tournament-signup, .tournament-check-in, .admin-panel)
  :is(input, textarea, select):focus-visible {
  border-style: solid;
  border-color: var(--accent-ink);
}

.page-content
  :is(.form-question, .account-field, .store-add, .store-cart, .tournament-signup, .tournament-check-in, .admin-panel)
  :is(input, textarea, select)[aria-invalid="true"] {
  border-color: var(--error);
}

/* Checkboxes and choices drawn here: a red square or dot when on. */
.page-content .form-option input {
  appearance: none;
  display: grid;
  place-content: center;
  flex: none;
  width: 16px;
  height: 16px;
  margin: 0;
  border: 1px solid var(--fg-muted);
  border-radius: var(--space-1);
  background: transparent;
}

.page-content .form-option input[type="radio"] {
  border-radius: 50%;
}

.page-content .form-option input::before {
  content: "";
  width: var(--space-2);
  height: var(--space-2);
  border-radius: 2px;
  background: var(--accent);
  transform: scale(0);
  transition: transform 0.1s;
}

.page-content .form-option input[type="radio"]::before {
  border-radius: 50%;
}

.page-content .form-option input:checked::before {
  transform: scale(1);
}

.page-content .form-option input[aria-invalid="true"] {
  border-color: var(--error);
}

/* Buttons: lowercase, the store's too. */
.page-content
  :is(.form, .account-form, .account-settings, .store-packages, .store-package, .store-cart, .store-subscriptions, .tournament-signup, .tournament-check-in, .admin-panel)
  :is(button, [data-slot="button"]) {
  text-transform: lowercase;
}

/* The account page's other buttons, such as "sign out", the cart's
   "update" and "remove", moving to another tier, and the tier card's
   "change payment method" and "cancel", as dashed pills like the nav's,
   red when pointed at. */
.page-content
  :is(.account-form, .account-settings, .store-cart, .store-tier-switch, .store-subscription-actions, .tournament-signup, .tournament-check-in, .admin-panel)
  [data-slot="button"]:not(.bg-primary, form[data-provider] > *) {
  height: auto;
  padding: var(--space-1) 6px;
  border: 1px dashed color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: var(--round-small);
  background: none;
  color: var(--fg-muted);
  font-size: var(--text-caption);
  line-height: 1.625;
  font-weight: 400;
}

.page-content
  :is(.account-form, .account-settings, .store-cart, .store-tier-switch, .store-subscription-actions, .tournament-signup, .tournament-check-in, .admin-panel)
  [data-slot="button"]:not(.bg-primary, form[data-provider] > *):hover {
  border-color: var(--accent-ink);
  background: none;
  color: var(--accent-ink);
}

/* The responses pages, for admins (/forms/responses): pixel headings, the
   answers in the case they were sent, and each question's column wide
   enough that its label stays a few lines. */
.form-table td {
  text-transform: none;
}

.form-table th[data-name] {
  min-width: 16rem;
}

/* The forms home's rows (forms/index.md): a name and a line about it,
   then a dashed pill to the form. */
.page-content .columns.form-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
}

.page-content .form-row > .column {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.page-content .form-row > .column > * + * {
  margin-top: 0;
}

.page-content .form-row-name {
  font-size: var(--text-label);
  line-height: 1.33;
  color: var(--fg);
}

.page-content .form-row-go {
  flex: none;
  padding: var(--space-1) 6px;
  border: 1px dashed var(--line);
  border-radius: var(--round-small);
  white-space: nowrap;
}

.page-content .form-row-go:has(a:hover) {
  border-color: var(--line-hover);
}

.page-content .form-row-go a:not([data-slot="button"]) {
  color: var(--fg-muted);
  text-decoration: none;
}

.page-content .form-row-go a:not([data-slot="button"])::after {
  content: " →";
  content: " →" / "";
}

.page-content .form-row-go a:not([data-slot="button"]):hover {
  color: var(--fg);
}

/* Notes only admins see, the forms' own ("you're an admin: see the
   responses") and forms/index.md's, a card of its own there since it's
   off a form's card: their link in red, and the card's edge faintly. */
.page-content .admin-note {
  padding: var(--space-2) var(--space-4);
  border: 1px dashed color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-label);
}

.page-content :is(.form-admin, .admin-note) a:not([data-slot="button"]) {
  color: var(--accent-ink);
}

/* Success in the online green: a sent form's first words, and notices
   that something worked, such as a password saved. */
.page-content .form-confirmation > p:first-child,
.page-content .account-notice[data-kind="done"] {
  color: var(--ok);
}

.page-content .account-notice[data-kind="done"] {
  border-color: color-mix(in srgb, var(--ok) 45%, transparent);
}

/* --- Store ---------------------------------------------------------------- */

/* The store module's tags (store/), drawn from the PayNow store: each tier
   a dashed card like the forms', its name and price in pixel type over its
   "subscribe" button. A product's own page, the cart and the store's word
   that PayNow can't be reached are cards too, in their small lowercase
   type. The main buttons ("subscribe", "check out at PayNow", confirming a
   move to another tier) are dressed in "Join buttons", the "how many" boxes
   and the small buttons (the cart's, moving tier, and the tier card's
   "change payment method" and "cancel") in "Forms". */
.page-content :is(.store-package-card, .store-package, .store-cart),
.page-content :is(.store-categories, .store-packages)[data-state="unavailable"] {
  padding: var(--space-4);
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-label);
  line-height: 1.625;
  text-transform: lowercase;
}

/* Bold text in them, such as the sections of a tier's perks in its
   description from PayNow, is ink and lowercase, not the name's red. */
.page-content :is(.store-package-card, .store-package, .store-cart) strong {
  color: var(--fg);
  text-transform: inherit;
}

/* Packages in rows that wrap, as many to a row as fit: the tiers side by
   side, and the shop's packages in a grid like its subcategories
   ("Categories", below). */
.page-content .store-package-list {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
  gap: var(--space-3);
}

/* The grid spaces the cards, not the gap the app puts between list items,
   which would set every card but the first lower. */
.page-content .store-package-list > li {
  margin: 0;
}

.page-content .store-package-card {
  gap: var(--space-2);
}

.page-content .store-package-name {
  margin: 0;
  font-family: var(--font-pixel);
  font-size: var(--text-body);
  line-height: 1.43;
  font-weight: 400;
  color: var(--fg);
}

.page-content .store-package-name a:not([data-slot="button"]) {
  text-decoration-color: transparent;
}

.page-content .store-package-name a:not([data-slot="button"]):hover {
  text-decoration-color: var(--fg);
}

/* A muted arrow after each package's name, like the categories' cards, as
   it links to the package's own page (package.md) with its description
   from PayNow. Inline-block keeps the hover underline off it, and its empty
   alt text has screen readers say only the name. */
.page-content .store-package-name a:not([data-slot="button"])::after {
  content: "→";
  content: "→" / "";
  display: inline-block;
  margin-left: 6px;
  color: var(--fg-muted);
  transition: color 0.15s;
}

.page-content .store-package-name a:not([data-slot="button"]):hover::after {
  color: var(--fg);
}

/* The price in pixel type, in ink, and its labels ("subscription · renews
   automatically") as dashed pills like the features'. */
.page-content .store-price {
  gap: var(--space-2);
}

.page-content .store-price strong {
  font-family: var(--font-pixel);
  font-size: 24px;
  line-height: 1;
  color: var(--fg);
}

.page-content .store-badge {
  margin: 0;
  padding: 2px 6px;
  border: 1px dashed var(--line);
  border-radius: var(--round-small);
  background: none;
  font-size: var(--text-caption);
  line-height: 1.625;
  white-space: nowrap;
  color: var(--fg-muted);
}

/* Each card's button at its foot, however long the tier's name. */
.page-content .store-package-card .store-add {
  margin-top: auto;
}

/* A product's slideshow on its own page (package.md's {% package %}),
   where PayNow has more than one picture of it: each picture whole in a
   dashed frame, the arrows over it dashed pills like the store's other
   small buttons, how far through it is a pill like the badges, and the
   small pictures under it dashed too, the one shown edged solid in red. */
.page-content .store-gallery-track {
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: color-mix(in srgb, var(--bg) 25%, transparent);
}

.page-content .store-gallery-track:focus-visible {
  outline: 2px solid var(--accent-ink);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--bg);
}

.page-content .store-gallery-slide .store-image {
  max-height: 420px;
  border-radius: 0;
}

.page-content :is(.store-gallery-prev, .store-gallery-next)[data-slot="button"] {
  border: 1px dashed color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: var(--round-small);
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  color: var(--fg-muted);
}

.page-content
  :is(.store-gallery-prev, .store-gallery-next)[data-slot="button"]:hover {
  border-color: var(--accent-ink);
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  color: var(--accent-ink);
}

.page-content .store-gallery-count {
  margin: 0;
  padding: 2px 6px;
  border: 1px dashed var(--line);
  border-radius: var(--round-small);
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  font-size: var(--text-caption);
  line-height: 1.625;
  color: var(--fg-muted);
}

.page-content .store-gallery-thumb {
  padding: 2px;
  border: 1px dashed color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: var(--round-small);
}

.page-content .store-gallery-thumb:hover {
  border-color: var(--accent-ink);
}

.page-content .store-gallery-thumb[aria-current] {
  border-style: solid;
  border-color: var(--accent-ink);
}

.page-content .store-gallery-thumb img {
  width: 64px;
  height: 36px;
  border-radius: var(--space-1);
}

/* Small mono captions: the words over the tiers that buying one takes
   being signed in and a linked Discord account, the note under the perks,
   and what "subscribe", moving tier and the tier card's buttons say once
   pressed. */
.page-content
  :is(.store-sign-in, .store-discord, .store-tier, .store-add p, .store-tier-switch p, .store-subscription-actions p, .store-subscription-done, .perks-note) {
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
}

/* The link to the cart under the tiers, with how much is in it: a dashed
   pill like the features', muted like the nav's links. */
.page-content a.store-more {
  display: block;
  width: fit-content;
  padding: var(--space-1) 6px;
  border: 1px dashed var(--line);
  border-radius: var(--round-small);
  color: var(--fg-muted);
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
  text-decoration: none;
}

.page-content a.store-more:hover {
  border-color: var(--line-hover);
  color: var(--fg);
}

/* The cart: a plain table like the leaderboards', its total in ink. */
.page-content .store-cart {
  overflow-x: auto;
}

.page-content .store-cart-table {
  width: 100%;
}

/* Its table and notes at the card's size, not the app's larger ones. */
.page-content .store-cart :is(table, p) {
  font-size: inherit;
}

.page-content .store-cart-table :is(th, td) {
  padding: var(--space-1) var(--space-2);
  border: 0;
  border-top: 1px dashed var(--line);
  background: none;
  font-weight: 400;
  text-align: left;
  vertical-align: baseline;
}

.page-content .store-cart-table :is(th, td):first-child {
  padding-left: 0;
}

.page-content .store-cart-table :is(th, td):last-child {
  padding-right: 0;
}

.page-content .store-cart-table thead th {
  border-top: 0;
  color: var(--fg-muted);
}

.page-content .store-cart-table .store-cart-price {
  text-align: right;
}

/* A paid cart's thanks, in the online green like a sent form's. */
.page-content .store-cart[data-state="paid"] > p:first-child {
  color: var(--ok);
}

/* The signed-in visitor's tier at the top of the store's front page
   (index.md), a card like the about card: "your tier", then theirs from
   {% subscriptions %}, its name in pixel type like the tiers' and when it
   renews or ends muted, or, without one, that they have none. */
.page-content .callout.card.my-tier {
  padding: var(--space-3) var(--space-4);
}

.page-content .callout.card.my-tier > p {
  padding: 0;
}

.page-content .callout.card.my-tier > * + * {
  margin-top: var(--space-1);
}

.page-content .my-tier-label {
  font-size: var(--text-caption);
  color: var(--fg-muted);
  text-transform: lowercase;
}

.page-content .store-subscriptions {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-label);
  text-transform: lowercase;
}

.page-content .store-subscription {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0;
}

.page-content .store-subscription-name {
  font-family: var(--font-pixel);
  font-size: var(--text-h2);
  line-height: 1.4;
  color: var(--fg);
  text-transform: lowercase;
}

.page-content .store-subscription-name a:not([data-slot="button"]) {
  text-decoration-color: transparent;
}

.page-content .store-subscription-name a:not([data-slot="button"]):hover {
  text-decoration-color: var(--fg);
}

.page-content .store-subscription-when {
  color: var(--fg-muted);
}

/* The perks tables on the store's front page (index.md), one card a group
   (in-game, Discord, Discord's custom slots) like {% player-stats %}, 6px
   apart: the group's name in pixel type over its perks, then a column a
   tier, the same width in every card so the tiers line up down the run.
   Each perk is ticked or counted, its cell left empty where the tier
   hasn't it, for a faint dash, so what each tier adds steps out. A row
   pointed at is washed red, like a leaderboard's. */
.page-content table.perks {
  width: 100%;
  table-layout: fixed;
  border-collapse: separate;
  border-spacing: 0;
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
}

.page-content > table.perks + table.perks {
  margin-top: 6px;
}

.page-content .perks :is(th, td) {
  padding: var(--space-1) var(--space-2);
  border: 0;
  border-top: 1px dashed var(--line);
  background: none;
  font-weight: 400;
  text-align: center;
  vertical-align: baseline;
  color: var(--fg);
}

.page-content .perks :is(th, td):first-child {
  padding-left: var(--space-4);
  text-align: left;
}

/* The tiers' columns: 8% of the card, the last 10% for the card's own
   padding inside it (a calc() of % and px counts as no width at all in a
   table), and 24px on phones, so the perks keep room to read. */
.page-content .perks :is(th, td):not(:first-child) {
  width: 8%;
  padding-inline: 0;
}

.page-content .perks :is(th, td):last-child {
  width: 10%;
  padding-right: var(--space-4);
}

@media (width < 40rem) {
  .page-content .perks :is(th, td):not(:first-child) {
    width: 24px;
  }

  .page-content .perks :is(th, td):last-child {
    width: 40px;
  }
}

.page-content .perks thead th {
  padding-top: var(--space-3);
  border-top: 0;
  vertical-align: bottom;
  color: var(--fg-muted);
}

.page-content .perks thead th:first-child {
  font-family: var(--font-pixel);
  font-size: var(--text-body);
  line-height: 1.43;
  color: var(--fg);
}

.page-content .perks td:empty::before {
  content: "—";
  content: "—" / "";
  color: var(--fg-faint);
}

.page-content .perks tbody tr:hover {
  background: var(--accent-wash);
}

.page-content .perks tbody tr:last-child > * {
  padding-bottom: var(--space-3);
}

/* Categories ({% categories %}): the "items" category's subcategories in
   the shop under the tiers on the store's front page (index.md), and a
   category's subcategories on its page, in rows that wrap like the
   packages'. Each a dashed card like a tier's, its name in pixel type and
   a muted arrow, as it's a link that goes somewhere. */
.page-content .store-categories {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
  gap: var(--space-3);
}

.page-content .store-categories > li {
  margin: 0;
}

.page-content a.store-category {
  height: 100%;
  padding: var(--space-4);
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-family: var(--font-pixel);
  font-size: var(--text-body);
  line-height: 1.43;
  font-weight: 400;
  color: var(--fg);
  text-transform: lowercase;
  text-decoration: none;
}

.page-content a.store-category::after {
  content: "→";
  content: "→" / "";
  margin-left: auto;
  color: var(--fg-muted);
}

.page-content a.store-category:hover {
  border-color: var(--line-hover);
  background: var(--card);
}

.page-content a.store-category:hover::after {
  color: var(--fg);
}

/* The front page's shop goes, heading and all, while "items" has neither
   subcategories nor packages: {% categories %} then draws nothing, and
   {% packages %} only that nothing's for sale. */
.page-content .store-packages.shop:not(:has(.store-package-list)) {
  display: none;
}

.page-content
  h2.shop-heading:not(
    :has(~ .store-categories.shop, ~ .store-packages.shop .store-package-list)
  ) {
  display: none;
}

/* --- Tournaments ---------------------------------------------------------- */

/* The tournament module's tags (tournament/), and its {% tournament-list %}
   on the home page. Each tournament in a list is a dashed card in a column
   of them, 6px apart like the forms' rows: its name in pixel type with its
   status, then its game, entries and start, its description (paragraphs,
   line breaks and links, as on its page), and a muted arrow. The name links
   its page, stretched over the whole card like the server list's, and the
   description's links sit above it. Names of players and teams keep their
   case, as players' do on the leaderboards. */
.page-content ul.tournament-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 0;
}

.page-content .tournament-list > li {
  margin: 0;
}

.page-content .tournament-card {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--space-1) var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  color: var(--fg-muted);
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
  transition: border-color 0.15s;
}

.page-content .tournament-card:is(:hover, :focus-within) {
  border-color: var(--line-hover);
  background: var(--card);
  color: var(--fg-muted);
}

.page-content .tournament-card > * {
  grid-column: 1;
}

.page-content .tournament-card::after {
  content: "→";
  content: "→" / "";
  grid-column: 2;
  grid-row: 1 / span 3;
  color: var(--fg-muted);
  transition: color 0.15s;
}

.page-content .tournament-card:is(:hover, :focus-within)::after {
  color: var(--fg);
}

.page-content .tournament-card-name {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-pixel);
  font-size: var(--text-body);
  line-height: 1.43;
  font-weight: 400;
  color: var(--fg);
}

.page-content a.tournament-card-link {
  color: inherit;
  text-decoration: none;
}

.page-content .tournament-card-link:focus-visible {
  outline: none;
  box-shadow: none;
}

.page-content .tournament-card:has(.tournament-card-link:focus-visible) {
  outline: 2px solid var(--accent-ink);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--bg);
}

.page-content .tournament-card-meta,
.page-content .tournament-card-description {
  font-size: inherit;
}

.page-content .tournament-card-description {
  gap: var(--space-2);
}

/* A list with nothing in it ("no tournament is coming up"), or that the
   database couldn't give: one line in a card, like an empty board. */
.page-content p.tournament-list,
.page-content p.tournament-participants {
  padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
}

/* A tournament's status as a dashed pill, like the store's badges: muted
   while it's coming up or over, and while it's being played, the online
   green with a dot before it, like the server's status. */
.page-content .tournament-status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  padding: 0 6px;
  border: 1px dashed var(--line);
  border-radius: var(--round-small);
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  line-height: 1.625;
  font-weight: 400;
  color: var(--fg-muted);
  text-transform: lowercase;
  vertical-align: baseline;
}

.page-content .tournament-status[data-status="live"] {
  border-color: color-mix(in srgb, var(--ok) 45%, transparent);
  color: var(--ok);
}

.page-content .tournament-status[data-status="live"]::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 9999px;
  background: var(--ok);
}

/* {% tournament-details %} at the top of a tournament's page: its
   description, then its facts in a card, labels muted and values in ink,
   then its stages. */
.page-content .tournament-details {
  font-size: var(--text-label);
  line-height: 1.625;
  text-transform: lowercase;
}

.page-content .tournament-details > p {
  max-width: 65ch;
}

.page-content .tournament-facts {
  gap: var(--space-1) var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-caption);
  line-height: 1.625;
}

.page-content .tournament-facts dt {
  font-weight: 400;
  color: var(--fg-muted);
}

.page-content .tournament-facts dd {
  margin: 0;
  color: var(--fg);
}

.page-content .tournament-outline {
  padding-left: var(--space-6);
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
}

.page-content .tournament-outline .font-medium {
  font-weight: 400;
  color: var(--fg);
}

/* {% signup %} and {% check-in %}: cards like the forms', each part headed
   in pixel type ("players", "watch", "check-in"). The main buttons ("sign
   up", "make the team", "check in") are dressed in "Join buttons", the
   fields and small buttons ("leave", "new code") in "Forms". */
.page-content :is(.tournament-signup, .tournament-check-in) {
  max-width: none;
  padding: var(--space-4);
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-label);
  line-height: 1.625;
  text-transform: lowercase;
}

.page-content > .tournament-signup + .tournament-check-in {
  margin-top: 6px;
}

.page-content :is(.tournament-signup, .tournament-check-in) :is(p, li, label) {
  font-size: inherit;
}

.page-content :is(.tournament-signup, .tournament-check-in) label {
  font-weight: 400;
  color: var(--fg);
}

.page-content .tournament-heading {
  margin: 0;
  font-family: var(--font-pixel);
  font-size: var(--text-body);
  line-height: 1.43;
  font-weight: 400;
  letter-spacing: normal;
  color: var(--fg);
}

/* The boxes to tick before signing up (tournament.md's {% agree %} lines),
   above the buttons: drawn like the forms' checkboxes (.form-option, in
   "Forms"), each line in ink, its rules link underlined like any. */
.page-content .tournament-agreements {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.page-content .tournament-agreements > * {
  margin: 0;
}

.page-content .tournament-agree {
  align-items: flex-start;
  gap: var(--space-2);
  line-height: 1.625;
}

.page-content .tournament-agree input {
  margin-top: 2px;
}

.page-content .tournament-agree label {
  font-size: inherit;
  line-height: inherit;
  color: var(--fg);
}

.page-content .tournament-roster,
.page-content .tournament-participant-name {
  text-transform: none;
}

/* A team's join code, as a dashed pill like the server's address, in the
   case it's typed in. */
.page-content .tournament-code code {
  padding: 2px 6px;
  border: 1px dashed var(--line);
  border-radius: var(--round-small);
  font-weight: 400;
  text-transform: none;
}

/* A match: its two sides in a dashed box like a card's, the winner in ink
   and the rest muted, scores in ink. While it's played, its edge and its
   "live" are the online green. Admins click one to score it (match.tsx),
   so theirs turn red when pointed at, like the small buttons. */
.page-content .tournament-match {
  border: 1px dashed var(--line);
  border-radius: var(--round-small);
  background: var(--card);
  font-size: var(--text-caption);
  line-height: 1.625;
  color: var(--fg-muted);
}

.page-content button.tournament-match:hover {
  border-color: var(--accent-ink);
}

.page-content .tournament-match[data-status="live"] {
  border-color: color-mix(in srgb, var(--ok) 60%, transparent);
}

.page-content .tournament-match-meta,
.page-content .tournament-match-note {
  border-color: var(--line);
  border-style: dashed;
  font-size: var(--text-caption);
  color: var(--fg-muted);
  text-transform: lowercase;
}

.page-content .tournament-side {
  padding: var(--space-1) var(--space-2);
}

.page-content .tournament-side + .tournament-side {
  border-top: 1px dashed var(--line);
}

.page-content .tournament-side[data-won] {
  font-weight: 400;
  color: var(--fg);
}

.page-content .tournament-side[data-empty] {
  font-style: normal;
  text-transform: lowercase;
}

.page-content .tournament-side-score {
  color: var(--fg);
}

.page-content .tournament-live-badge {
  padding: 0 6px;
  border-radius: var(--round-small);
  background: var(--ok);
  color: var(--on-accent);
  font-size: var(--text-caption);
  line-height: 1.43;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: lowercase;
}

/* A stage: its name in pixel type over what it is, muted; then its
   bracket's rounds side by side, each named in muted lowercase, joined by
   faint lines, or its groups' standings and rounds. */
.page-content .tournament-stage-name {
  font-family: var(--font-pixel);
  font-size: var(--text-body);
  line-height: 1.43;
  font-weight: 400;
  color: var(--fg);
}

.page-content .tournament-stage-meta,
.page-content .tournament-round-name,
.page-content .tournament-next,
.page-content :is(.tournament-bracket-view, .tournament-matches) > p {
  font-size: var(--text-caption);
  line-height: 1.625;
  letter-spacing: normal;
  color: var(--fg-muted);
  text-transform: lowercase;
}

.page-content .tournament-part {
  font-size: var(--text-label);
  font-weight: 400;
  color: var(--fg);
  text-transform: lowercase;
}

.page-content .tournament-pair::after,
.page-content .tournament-slot[data-in]::before,
.page-content .tournament-slot[data-out]::after {
  border-color: var(--fg-faint);
}

.page-content .tournament-placements {
  font-size: var(--text-caption);
  color: var(--fg);
}

.page-content .tournament-place {
  font-weight: 400;
  color: var(--fg-muted);
}

.page-content .tournament-round-list > summary {
  font-size: var(--text-caption);
  font-weight: 400;
  color: var(--fg-muted);
  text-transform: lowercase;
}

/* A round robin's or swiss's standings: a plain table in a card, like the
   leaderboards', the places of those going on in red and those out muted
   and struck through. */
.page-content .tournament-standings {
  border-collapse: separate;
  border-spacing: 0;
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-caption);
  line-height: 1.625;
}

.page-content .tournament-standings :is(th, td) {
  padding: var(--space-1) var(--space-2);
  border: 0;
  border-top: 1px dashed var(--line);
  background: none;
  font-weight: 400;
  vertical-align: baseline;
}

.page-content .tournament-standings :is(th, td):first-child {
  padding-left: var(--space-4);
}

.page-content .tournament-standings :is(th, td):last-child {
  padding-right: var(--space-4);
}

.page-content .tournament-standings thead th {
  border-top: 0;
  color: var(--fg-muted);
  text-transform: lowercase;
}

.page-content .tournament-standings td {
  color: var(--fg);
}

.page-content .tournament-standings tr[data-advancing] td:first-child {
  border-left: 0;
  color: var(--accent-ink);
}

/* {% participants %}: one card, a dashed row each, numbered in muted ink
   like the rules; a team's members under its name, muted. */
.page-content ol.tournament-participants {
  padding: 0 var(--space-4);
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-label);
  line-height: 1.625;
  list-style: none;
  counter-reset: entry;
}

.page-content ol.tournament-participants > li {
  position: relative;
  margin: 0;
  padding: var(--space-2) 0 var(--space-2) calc(2ch + var(--space-4));
  font-size: inherit;
  color: var(--fg);
  counter-increment: entry;
}

.page-content ol.tournament-participants > li + li {
  border-top: 1px dashed var(--line);
}

.page-content ol.tournament-participants > li::before {
  content: counter(entry, decimal-leading-zero);
  content: counter(entry, decimal-leading-zero) / "";
  position: absolute;
  left: 0;
  color: var(--fg-muted);
}

.page-content .tournament-participant-name {
  font-weight: 400;
}

.page-content .tournament-participants :is(.text-muted-foreground, .tournament-checked, .tournament-participant-members) {
  font-size: var(--text-caption);
  text-transform: lowercase;
}

.page-content .tournament-participant-members {
  text-transform: none;
}

.page-content .tournament-checked[data-checked] {
  color: var(--ok);
}

/* --- Admin ---------------------------------------------------------------- */

/* The admin module's pages (admin/), which only admins see: its tags drawn
   like the rest of the site's. Their parts are headed in pixel type, their
   tables are plain ones in cards like the leaderboards', each module's link
   on the admin home is a card like the store's categories, and what's
   wrong or right says so in the status colours, as dashed pills. Fields,
   main buttons and small ones are dressed in "Forms" and "Join buttons". */
.page-content .admin-panel {
  font-size: var(--text-label);
  line-height: 1.625;
}

.page-content .admin-panel h2 {
  font-family: var(--font-pixel);
  font-size: var(--text-h2);
  line-height: 1.55;
  font-weight: 400;
  letter-spacing: normal;
  color: var(--fg);
}

.page-content .admin-panel h3 {
  font-family: var(--font-pixel);
  font-size: var(--text-body);
  line-height: 1.43;
  font-weight: 400;
  letter-spacing: normal;
  color: var(--fg);
}

.page-content .admin-panel :is(p, li, label, summary) {
  font-size: inherit;
}

.page-content .admin-panel label {
  color: var(--fg);
}

.page-content .admin-table {
  border-collapse: separate;
  border-spacing: 0;
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  font-size: var(--text-caption);
  line-height: 1.625;
}

.page-content .admin-table :is(th, td) {
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-top: 1px dashed var(--line);
  background: none;
  font-weight: 400;
}

.page-content .admin-table thead th {
  border-top: 0;
  color: var(--fg-muted);
  text-transform: lowercase;
}

.page-content .admin-table td {
  color: var(--fg);
}

.page-content .admin-badge {
  padding: 0 6px;
  border: 1px dashed var(--line);
  border-radius: var(--round-small);
  font-size: var(--text-caption);
  line-height: 1.625;
  font-weight: 400;
  color: var(--fg-muted);
  text-transform: lowercase;
}

.page-content .admin-badge[data-kind="good"] {
  border-color: color-mix(in srgb, var(--ok) 45%, transparent);
  color: var(--ok);
}

.page-content .admin-badge[data-kind="warn"] {
  border-color: color-mix(in srgb, var(--warn) 45%, transparent);
  color: var(--warn);
}

.page-content .admin-badge[data-kind="bad"] {
  border-color: color-mix(in srgb, var(--error) 45%, transparent);
  color: var(--error);
}

.page-content .admin-section-list {
  gap: 6px;
}

.page-content a.admin-section {
  padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--line);
  border-radius: var(--round);
  background: var(--card);
  color: var(--fg-muted);
  font-size: var(--text-caption);
  line-height: 1.625;
  text-transform: lowercase;
  text-decoration: none;
  transition: border-color 0.15s;
}

.page-content a.admin-section:hover {
  border-color: var(--line-hover);
  background: var(--card);
  color: var(--fg-muted);
}

.page-content .admin-section-title {
  font-family: var(--font-pixel);
  font-size: var(--text-body);
  line-height: 1.43;
  font-weight: 400;
  color: var(--fg);
}

.page-content .admin-section-title::after {
  content: " →";
  content: " →" / "";
  color: var(--fg-muted);
}

.page-content :is(.admin-section-summary, .admin-section-description) {
  font-size: inherit;
  font-weight: 400;
}

.page-content .admin-section-summary {
  color: var(--fg);
}

.page-content .admin-facts {
  font-size: var(--text-caption);
}

.page-content .admin-facts dt {
  font-weight: 400;
  color: var(--fg-muted);
}

.page-content .admin-facts dd {
  color: var(--fg);
}

/* Their plain fields and buttons ("set", "add an entry", "search"), as the
   forms' dashed fields and the small dashed buttons. */
.page-content .admin-input {
  border: 1px dashed var(--fg-muted);
  border-radius: var(--round-small);
  background: transparent;
  color: var(--fg);
  font-family: var(--font-mono);
}

.page-content .admin-submit {
  border: 1px dashed color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: var(--round-small);
  background: none;
  color: var(--fg-muted);
  font-size: var(--text-caption);
  font-weight: 400;
  text-transform: lowercase;
}

.page-content .admin-submit:hover {
  border-color: var(--accent-ink);
  background: none;
  color: var(--accent-ink);
}

/* --- Reduced motion ------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition: none !important;
    animation: none !important;
  }
}
