
:root{
  /* Ground, in five steps. Each is a real surface, not a tint of the last. */
  --bg:#0a0a0c;--raise:#101013;--card:#141416;--line:#232327;--line-soft:#1b1b1f;
  --fg:#f2f3f5;--muted:#8a8d93;--dim:#7c7f86;--red:#e01e37;--deep:#7a0512;
  /* The switch track, the lit edge of an "on" card and the white a light button
     brightens to: three more greys, named so the dashboard paints from the same
     list as everything else rather than from hex written where it was needed. */
  --track:#1d1d21;--lit:#19191c;--hi:#ffffff;
  --ring:color-mix(in srgb,var(--red) 55%,transparent);
  --shell:min(1120px,100% - 48px);

  /* One spacing scale, so vertical rhythm is a decision rather than a habit. */
  --s1:6px;--s2:10px;--s3:16px;--s4:24px;--s5:38px;--s6:60px;--s7:96px;

  /* Two easings. Standard for anything that moves a short way, spring for
     anything a pointer is touching, so hover feels answered rather than faded. */
  --ease:cubic-bezier(.2,.7,.3,1);
  --spring:cubic-bezier(.34,1.4,.5,1);

  /* Depth. The inner hairline is what separates a raised surface from a
     lighter rectangle, and it is the single biggest tell of a dark UI that has
     been designed rather than coloured in. */
  --lift:0 1px 2px rgba(0,0,0,.5),0 8px 24px -12px rgba(0,0,0,.7);
  --lift-hi:0 2px 4px rgba(0,0,0,.5),0 18px 44px -16px rgba(0,0,0,.85);
  --inner:inset 0 1px 0 rgba(255,255,255,.045);
}
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:88px;
  scrollbar-color:var(--line) transparent;scrollbar-width:thin}
body{margin:0;background:var(--bg);color:var(--fg);
  font:16px/1.65 ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
  font-synthesis-weight:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  -webkit-tap-highlight-color:transparent;
  min-height:100svh;display:flex;flex-direction:column;overflow-x:hidden;
  /*
   * A long word breaks rather than overflowing its box.
   *
   * German and Russian found this: "Nutzungsbedingungen" is 158px of one
   * unbreakable word and the footer's Legal column is 123px at 768px wide, so
   * the word was simply cut off — and body's overflow-x:hidden meant there was
   * no scrollbar to reveal it. Nothing in the markup was wrong; the site is
   * just written in thirty languages now and English is the short one.
   *
   * Set here rather than on the one column, because the next long word will be
   * somewhere else. It only ever applies where a word would otherwise overflow,
   * so ordinary text is untouched.
   */
  overflow-wrap:break-word}
a{color:inherit;text-decoration:none}
h1,h2,h3{margin:0;letter-spacing:-.022em;line-height:1.12;text-wrap:balance;font-weight:650}
p{margin:0;text-wrap:pretty}
img{display:block}
::selection{background:color-mix(in srgb,var(--red) 40%,transparent);color:#fff}
.wrap{width:var(--shell);margin-inline:auto}

/* ── navigation ─────────────────────────────────────────────────────────── */
.nav{position:sticky;top:0;z-index:20;
  background:color-mix(in srgb,var(--bg) 72%,transparent);
  border-bottom:1px solid transparent;
  transition:border-color .2s var(--ease),background .2s var(--ease)}
@supports (backdrop-filter:blur(1px)){
  .nav{backdrop-filter:blur(16px) saturate(160%);border-bottom-color:var(--line-soft)}
}
@supports not (backdrop-filter:blur(1px)){
  .nav{background:var(--bg);border-bottom-color:var(--line-soft)}
}
/*
 * min-height and flex-wrap, not height.
 *
 * A fixed height with no wrap is only correct while the items happen to fit,
 * and "happen to fit" changed the day the nav grew a fourth link. The bar is
 * now robust at every width by construction rather than tuned to the widths
 * somebody thought to check — which is the failure recorded in CLAUDE.md as
 * "a responsive check run at exactly one width".
 *
 * No backticks in this comment, and none anywhere in this stylesheet: the whole
 * of it is one template literal, so a backtick ends the string four lines into
 * whatever it was documenting. That has now happened twice.
 */
.nav .wrap{--nav-gap:var(--s1);display:flex;align-items:center;gap:var(--nav-gap);
  min-height:64px;flex-wrap:wrap;row-gap:2px}
/*
 * Buttons sit further apart than links. Six pixels is right between two words
 * and cramped between two bordered pills: Add to Discord ran into Menu on a
 * phone and into Dashboard on a desktop, close enough to read as one control.
 * Each button and the account chip make up the difference to the next step up
 * the scale, from whatever the bar's own gap is at that width — the narrow
 * rule below shrinks it to 2px, and a margin worked out from the wide gap left
 * the phone at the 6px it started with.
 */
.nav .wrap > .btn,.nav .wrap > .acct,.nav .menu > .btn{
  margin-left:calc(var(--s2) - var(--nav-gap))}
/* The short label of the primary action, for a bar too narrow for the long one.
   The link keeps the long one as its name either way (aria-label). */
.addshort{display:none}
.nav.terse .addfull{display:none}
.nav.terse .addshort{display:inline}
.brand{display:flex;align-items:center;gap:var(--s2);font-weight:680;letter-spacing:-.03em;
  margin-right:auto;font-size:16.5px}
.brand img{width:28px;height:28px;border-radius:50%;
  box-shadow:0 0 0 1px var(--line),0 4px 12px -4px var(--ring)}
.nav a.link{padding:8px 12px;border-radius:9px;color:var(--muted);font-size:14.5px;
  transition:color .15s var(--ease),background .15s var(--ease)}
.nav a.link:hover{color:var(--fg);background:var(--line-soft)}
/*
 * aria-current alone told a screen reader which page this is and told everybody
 * else nothing. The underline is the visible half of the same fact.
 */
.nav a.link[aria-current="page"]{color:var(--fg);
  box-shadow:inset 0 -2px 0 0 color-mix(in srgb,var(--fg) 55%,transparent)}

/* ── buttons ────────────────────────────────────────────────────────────── */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;
  padding:12px 20px;border-radius:11px;font-size:15px;font-weight:600;letter-spacing:-.01em;
  color:var(--fg);border:1px solid var(--line);background:var(--raise);white-space:nowrap;
  box-shadow:var(--inner);
  transition:transform .2s var(--spring),border-color .18s var(--ease),
    background .18s var(--ease),box-shadow .18s var(--ease)}
.btn:hover{transform:translateY(-2px);border-color:color-mix(in srgb,var(--fg) 24%,transparent);
  box-shadow:var(--inner),var(--lift)}
.btn:active{transform:translateY(0) scale(.985);transition-duration:.06s}
.btn.primary{background:linear-gradient(180deg,#f02742,var(--red) 45%,#a8142a);
  border-color:transparent;color:#fff;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22),0 10px 30px -12px var(--ring)}
.btn.primary:hover{box-shadow:inset 0 1px 0 rgba(255,255,255,.22),0 16px 38px -12px var(--ring)}
.btn.sm{padding:8px 15px;font-size:14px;border-radius:9px}
.btn.del{border-color:color-mix(in srgb,var(--red) 45%,var(--line));
  color:color-mix(in srgb,var(--red) 72%,var(--fg))}
.btn.del:hover{border-color:var(--red)}
:where(a,button,input,select,textarea,summary):focus-visible{outline:2px solid var(--red);outline-offset:3px;
  border-radius:9px}

/* ── hero ───────────────────────────────────────────────────────────────── */
/*
 * Four stacked radial gradients rather than one.
 *
 * A single radial reads as a spotlight on a flat colour. Overlapping fields at
 * different sizes and opacities give the light somewhere to fall off to, which
 * is what stops the top of the page looking like a placeholder.
 */
.hero{position:relative;overflow:hidden;padding:var(--s7) 0 84px;isolation:isolate;
  background:
    radial-gradient(58% 62% at 74% 22%,color-mix(in srgb,var(--red) 34%,transparent) 0%,transparent 70%),
    radial-gradient(70% 80% at 88% 46%,var(--deep) 0%,transparent 66%),
    radial-gradient(110% 96% at 50% 0%,#33060c 0%,transparent 60%),
    linear-gradient(180deg,#15060a 0%,var(--bg) 92%)}
/* A hairline of light along the very top, the way a lit surface actually is. */
.hero::before{content:"";position:absolute;inset:0 0 auto;height:1px;z-index:-1;
  background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--red) 60%,transparent),transparent)}
/* And the ground fading up into it, so the section boundary is not an edge. */
.hero::after{content:"";position:absolute;inset:auto 0 0;height:180px;z-index:-1;
  background:linear-gradient(180deg,transparent,var(--bg))}
.hero .wrap{position:relative;display:flex;align-items:center;gap:var(--s5) var(--s6);
  flex-wrap:wrap}
.hero .copy{min-width:0;flex:1 1 460px;max-width:62ch}
.mark{width:176px;height:176px;border-radius:50%;flex:none;margin-inline:auto;
  box-shadow:0 0 0 1px color-mix(in srgb,var(--fg) 12%,transparent),
    0 24px 60px -14px rgba(0,0,0,.85),0 0 70px -20px var(--ring)}
.hero h1{font-size:clamp(46px,9vw,82px);letter-spacing:-.05em;line-height:.98;
  background:linear-gradient(178deg,#fff 20%,#c9ccd2);
  -webkit-background-clip:text;background-clip:text;color:transparent}
.hero .lede{margin-top:var(--s3);color:#cdd0d6;font-size:clamp(16.5px,2.4vw,19.5px);max-width:52ch}
.tag{display:inline-flex;align-items:center;gap:8px;margin-bottom:18px;padding:5px 13px;
  border:1px solid color-mix(in srgb,var(--fg) 20%,transparent);border-radius:999px;
  font-size:11.5px;letter-spacing:.19em;text-transform:uppercase;color:#e6e7ea;
  background:color-mix(in srgb,#000 28%,transparent);box-shadow:var(--inner)}
.cta{display:flex;gap:12px;flex-wrap:wrap;margin-top:var(--s5)}
.stats{display:flex;gap:var(--s4);flex-wrap:wrap;margin-top:var(--s5);color:var(--muted);
  font-size:13.5px}
.stats span{padding-left:var(--s3);border-left:1px solid var(--line)}
.stats span:first-child{padding-left:0;border-left:0}
.stats b{display:block;color:var(--fg);font-size:25px;font-weight:680;letter-spacing:-.03em;
  font-variant-numeric:tabular-nums;line-height:1.2}

/* ── sections ───────────────────────────────────────────────────────────── */
main{flex:1;padding:var(--s6) 0 var(--s7)}
.eyebrow{display:block;font-size:11.5px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--dim);font-weight:650}
.section{margin-top:var(--s7)}
.section:first-of-type{margin-top:0}
.section > :is(h1,h2){font-size:clamp(25px,3.6vw,34px);margin-top:var(--s2)}
.section > .sub{margin-top:var(--s2);color:var(--muted);max-width:62ch}
/* Running-text links, one rule: the section subtitle, FAQ answers, step lists,
   legal prose, callouts, the translation note, the 404's page list and a card's
   paragraph all take the same underline, so a link reads as a link wherever it
   is. Never a button: the rule outranks .btn.solid, and a light button inside
   prose was drawn in its own background colour, label and all, on the login
   retry, the verify page's way back and the dashboard's error retry. */
:is(.section > .sub,.qa .body,ol.steps,.prose,.callout,.transnote,ul.plain,.card p) a:not(.btn){
  color:var(--fg);text-decoration:underline;text-underline-offset:3px;
  text-decoration-color:color-mix(in srgb,var(--fg) 35%,transparent)}
:is(.section > .sub,.qa .body,ol.steps,.prose,.callout,.transnote,ul.plain,.card p) a:not(.btn):hover{
  text-decoration-color:currentColor}

.grid{display:grid;gap:var(--s2);margin-top:var(--s4);
  grid-template-columns:repeat(auto-fit,minmax(min(258px,100%),1fr))}
.grid.top{align-items:start}
/*
 * The card, and the one line that does the work.
 *
 * The --inner shadow puts a one-pixel highlight along the top edge. Without it
 * surface on a dark ground is just a lighter rectangle; with it the surface
 * has a lit edge and reads as raised. The red only arrives on hover, which is
 * the palette's rule about rationing it and also simply better.
 */
.card{position:relative;background:var(--card);border:1px solid var(--line);border-radius:15px;
  padding:22px;display:flex;flex-direction:column;gap:7px;overflow:hidden;
  box-shadow:var(--inner);
  transition:border-color .2s var(--ease),transform .25s var(--spring),box-shadow .25s var(--ease)}
.card::before{content:"";position:absolute;inset:0 0 auto;height:1px;opacity:0;
  background:linear-gradient(90deg,transparent,var(--fg),transparent);
  transition:opacity .25s var(--ease)}
.card:hover{border-color:color-mix(in srgb,var(--fg) 18%,transparent);transform:translateY(-3px);
  box-shadow:var(--inner),var(--lift-hi)}
.card:hover::before{opacity:.85}
.card :is(h2,h3){font-size:16.5px;margin-bottom:7px;letter-spacing:-.015em}
.card p{color:var(--muted);font-size:14.5px}
/* The store's price line: .lede is a hero size elsewhere, which does not apply
   inside a card, so the figure gets its own weight here. Monochrome — the price
   is white on the card's grey, never red. */
.card .lede{margin-bottom:var(--s2);color:var(--muted);font-size:14px}
.card .lede b{color:var(--fg);font-size:22px;font-weight:680;letter-spacing:-.02em}
/* Both store cards end in a CTA; auto-top pins them to the same baseline even
   though one column has more bullets than the other. */
.card>.cta{margin-top:auto;padding-top:var(--s4)}
.card .meta{margin-top:auto;padding-top:var(--s3);display:flex;gap:6px;flex-wrap:wrap}

/* ── command reference: chips need no script, the search box is one ────── */
.syntax{display:flex;width:fit-content;max-width:100%;margin:var(--s4) 0 var(--s2);padding:3px;gap:2px;
  border:1px solid var(--line);border-radius:999px;background:var(--raise)}
.syntax a{flex:1 1 auto;padding:10px 16px;border-radius:999px;font-size:14px;text-align:center;
  color:var(--muted);text-decoration:none;overflow-wrap:anywhere;
  transition:color .15s var(--ease),background .15s var(--ease)}
.syntax a:hover{color:var(--fg)}
.syntax a[aria-current="page"]{color:var(--bg);background:var(--fg)}
.filters{display:flex;gap:8px;flex-wrap:wrap;margin:var(--s4) 0 var(--s1);
  border:0;padding:0;min-inline-size:0}
.filters input{position:absolute;opacity:0;pointer-events:none}
.filters label{padding:7px 15px;border:1px solid var(--line);border-radius:999px;font-size:14px;
  color:var(--muted);cursor:pointer;background:var(--raise);box-shadow:var(--inner);
  transition:color .15s var(--ease),border-color .15s var(--ease),
    background .15s var(--ease),transform .18s var(--spring)}
.filters label:hover{color:var(--fg);transform:translateY(-1px)}
.filters label:active{transform:translateY(0)}
.cmds:has(input[data-cat="all"]:checked) .cat{display:block}
.cmds:has(input[data-cat="all"]:checked) label[for="f-all"],
.cmds:has(input[data-cat="community"]:checked) label[for="f-community"],
.cmds:has(input[data-cat="config"]:checked) label[for="f-config"],
.cmds:has(input[data-cat="moderation"]:checked) label[for="f-moderation"],
.cmds:has(input[data-cat="utility"]:checked) label[for="f-utility"]{color:var(--bg);background:var(--fg);border-color:transparent}
.cmds .cat{display:none}
.cmds:has(input[data-cat="community"]:checked) .cat[data-cat="community"],
.cmds:has(input[data-cat="config"]:checked) .cat[data-cat="config"],
.cmds:has(input[data-cat="moderation"]:checked) .cat[data-cat="moderation"],
.cmds:has(input[data-cat="utility"]:checked) .cat[data-cat="utility"]{display:block}
.cat{margin-top:var(--s5)}
.cat > h2{font-size:12.5px;letter-spacing:.17em;text-transform:uppercase;color:var(--dim);
  margin-bottom:var(--s2)}
.cmd{border:1px solid var(--line);border-radius:12px;background:var(--card);margin-bottom:7px;
  overflow:hidden;box-shadow:var(--inner);
  transition:border-color .18s var(--ease),background .18s var(--ease)}
.cmd:hover{border-color:color-mix(in srgb,var(--fg) 14%,transparent)}
.cmd > summary{list-style:none;cursor:pointer;padding:13px 17px;display:flex;align-items:baseline;
  gap:12px;flex-wrap:wrap}
.cmd > summary::-webkit-details-marker{display:none}
.cmd > summary::after{content:"+";margin-left:auto;color:var(--dim);font-size:15px;line-height:1;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;transition:color .15s var(--ease)}
.cmd > summary:hover::after{color:var(--fg)}
.cmd[open] > summary::after{content:"−"}
.cmd[open] > summary{background:var(--raise)}
.cmd code.name{font-size:14.5px;color:var(--fg);font-weight:600}
.cmd .desc{color:var(--muted);font-size:14px;min-width:0;flex:1}
.cmd .body{padding:var(--s3) 17px 17px;border-top:1px solid var(--line-soft);color:var(--muted);
  font-size:14.5px}
.cmd .body > * + *{margin-top:12px}
.cmd .body strong{color:var(--fg);font-weight:600}
/* One pill rule for the whole site. There were two, and the dashboard's (lower
   down) silently overrode this one everywhere, its nowrap included — so a
   translated pill longer than its row, Russian's "— optional" at 320px, could
   not wrap and pushed the page wider than the phone. It wraps at its spaces
   now; inside the flex rows that hold pills that happens only when one pill is
   wider than the whole row. */
.pill{display:inline-block;padding:4px 10px;border-radius:999px;border:1px solid var(--line);
  font-size:12px;color:var(--dim);letter-spacing:.04em;background:var(--raise);max-width:100%}
.pill.perm{color:var(--muted)}
ul.plain{margin:0;padding-left:18px}
ul.plain li{margin-top:5px}

code,kbd{background:var(--raise);border:1px solid var(--line);border-radius:6px;padding:2px 7px;
  font-size:13px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}

/* ── questions ──────────────────────────────────────────────────────────── */
.qas{margin-top:var(--s4);border-top:1px solid var(--line-soft)}
.qa{border-bottom:1px solid var(--line-soft)}
.qa > summary{list-style:none;cursor:pointer;padding:18px 42px 18px 2px;position:relative;
  font-size:16px;font-weight:600;color:var(--fg);letter-spacing:-.012em;
  transition:color .15s var(--ease)}
.qa > summary::-webkit-details-marker{display:none}
.qa > summary:hover{color:#fff}
.qa > summary::after{content:"+";position:absolute;right:8px;top:50%;translate:0 -50%;
  color:var(--dim);font-size:18px;line-height:1;transition:color .15s var(--ease);
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.qa[open] > summary::after{content:"−";color:var(--fg)}
.qa .body{padding:0 42px 20px 2px;color:var(--muted);font-size:15px;max-width:68ch}

/*
 * Visually hidden, still read aloud. One definition, also used for the
 * wordmark of a bar too narrow for it (.nav.bare), where the link still needs
 * its name.
 */
.vh,.nav.bare .wordmark{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}

/* ── steps ──────────────────────────────────────────────────────────────── */
ol.steps{counter-reset:step;list-style:none;margin:var(--s4) 0 0;padding:0}
ol.steps li{counter-increment:step;position:relative;padding:0 0 var(--s4) 46px;
  border-left:1px solid var(--line);margin-left:14px}
ol.steps li:last-child{border-left-color:transparent;padding-bottom:0}
ol.steps li::before{content:counter(step);position:absolute;left:-14px;top:-2px;width:28px;height:28px;
  display:grid;place-items:center;border-radius:50%;background:var(--card);border:1px solid var(--line);
  font-size:13px;color:var(--fg);font-variant-numeric:tabular-nums;box-shadow:var(--inner)}
ol.steps :is(h2,h3){font-size:16.5px;letter-spacing:-.015em}
ol.steps p{color:var(--muted);font-size:14.5px;margin-top:6px}

/* ── prose (legal pages) ────────────────────────────────────────────────── */
.prose{max-width:70ch}
.prose h1{font-size:clamp(25px,3.6vw,34px);margin-top:var(--s2)}
.prose h2{font-size:19px;margin-top:var(--s5)}
.prose p,.prose li{color:var(--muted);margin-top:12px}
.prose strong{color:var(--fg);font-weight:600}
.prose .updated{margin-top:14px;color:var(--dim);font-size:13.5px}

.skip{position:absolute;top:0;left:0;z-index:40;padding:11px 18px;border-radius:0 0 11px 0;
  background:var(--card);border:1px solid var(--line);font-size:14.5px;font-weight:600;
  translate:0 -160%;transition:translate .18s var(--ease)}
.skip:focus{translate:0 0}

/* ── footer ─────────────────────────────────────────────────────────────── */
footer{border-top:1px solid var(--line-soft);padding:var(--s5) 0 var(--s6);color:var(--dim);
  font-size:14px;background:linear-gradient(180deg,transparent,color-mix(in srgb,var(--raise) 60%,transparent))}
/*
 * A grid, and an explicit column count — not auto-fit.
 *
 * This was display:flex on the wrap with repeat(auto-fit,minmax(140px,
 * max-content)) on the nav, and it put Product, Help and Legal in **one
 * column**, stacked, with two-thirds of the footer empty beside them. It looked
 * like a bug and it was one, and it shipped: auto-fit needs a definite
 * container width to work out how many columns fit, and a grid inside a flex
 * row is sized by its own content — indefinite — so the repetition resolves to
 * one. Every check passed, because nothing here measures where a thing landed.
 *
 * Two explicit columns for the whole footer and three for the links, dropping
 * at the widths below. No repetition count for a browser to guess at.
 */
footer .wrap{display:grid;grid-template-columns:minmax(220px,1fr) minmax(0,1.7fr);
  gap:var(--s5) var(--s6);align-items:start}
footer .about{max-width:34ch}
footer .about .brand{margin-bottom:var(--s2)}
footer nav{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s5) var(--s4)}
footer nav div{display:flex;flex-direction:column;gap:2px}
footer nav a{padding:4px 0}
footer nav b{color:var(--muted);font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;
  font-weight:650}
footer nav a:hover{color:var(--fg)}
footer a:hover{color:var(--fg)}

/* ── permalinks, permissions and the changelog ───────────────────────────── */
/*
 * The anchor is present in the markup for everybody and visible on hover or on
 * keyboard focus. Hiding it with display:none would take it out of the tab
 * order and out of a screen reader, which is the opposite of the point.
 */
.cmd .anchor{opacity:0;color:var(--dim);font-size:13px;padding:0 6px;border-radius:6px;
  transition:opacity .15s var(--ease),color .15s var(--ease)}
.cmd:hover .anchor,.cmd .anchor:focus-visible{opacity:1}
.cmd .anchor:hover{color:var(--fg)}
.cmd:target{border-color:color-mix(in srgb,var(--fg) 42%,transparent);background:var(--raise)}
/*
 * A permalink has to land somewhere visible. The category filter hides every
 * section but the checked one, so a link to a command in another category would
 * have scrolled to nothing at all — the filter is a display rule and the
 * fragment does not know about it.
 */
.cmds .cat:has(:target){display:block}
.cmd{scroll-margin-top:84px}

.perms{display:grid;gap:10px;margin-top:var(--s4)}
.perm-row{border:1px solid var(--line);border-radius:12px;background:var(--card);
  padding:16px 18px;box-shadow:var(--inner)}
.perm-row h2{margin:0;font-size:15.5px;letter-spacing:-.01em}
.perm-row .api{color:var(--dim);font-size:12.5px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.perm-row p{margin:8px 0 0;color:var(--muted);font-size:14.5px;max-width:70ch}
.perm-row p.limit{color:var(--dim)}
.used{margin-top:11px;display:flex;gap:6px;flex-wrap:wrap}

/* The connection-check form: one button, and the widget above it when there is one. */
.vform{margin-top:var(--s3);display:flex;flex-direction:column;align-items:flex-start;gap:14px}
.cf-turnstile{min-height:65px}
/*
 * The note on a translated page about what in it is not translated, or not
 * binding (translationNote). In the reader's own language, above the content,
 * and quiet rather than red: it is information, not an alarm. Its link — to
 * the English text of a policy — is underlined like every link in running
 * text, because the page's base rule takes the underline away.
 */
.transnote{margin:0 0 var(--s5);padding:13px 16px;border:1px solid var(--line);
  border-left:3px solid var(--dim);border-radius:0 12px 12px 0;background:var(--card);
  color:var(--muted);font-size:14.5px;max-width:74ch;box-shadow:var(--inner)}
.transnote strong{color:var(--fg);font-weight:600}

.rel{border-top:1px solid var(--line-soft);padding-top:var(--s4);margin-top:var(--s4)}
.rel:first-of-type{border-top:0;padding-top:0}
.rel time{color:var(--dim);font-size:12.5px;letter-spacing:.04em;text-transform:uppercase}
.rel h2{margin:6px 0 0;font-size:19px;letter-spacing:-.02em}
.rel ul{margin:12px 0 0;padding-left:18px;color:var(--muted);font-size:14.5px;max-width:72ch}
.rel li{margin-top:7px}

/*
 * The language picker, as plain links, one per language.
 *
 * Links rather than a select, and no script: a <select> that navigates needs
 * one, and a picker that does nothing without JavaScript would be worse than
 * the links it replaced. Each chip points at the page you are on, in that
 * language.
 */
.langs{grid-column:1/-1;margin-top:var(--s4);padding-top:var(--s4);
  border-top:1px solid var(--line-soft)}
/*
 * The summary is the control and the answer at once: it names the language you
 * are reading. The marker is a plus that becomes a minus, matching the command
 * rows and the FAQ rather than a triangle nothing else on the site uses.
 */
.langs > summary{display:flex;align-items:center;gap:10px;cursor:pointer;list-style:none;
  font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--dim);
  font-weight:650;transition:color .15s var(--ease)}
.langs > summary::-webkit-details-marker{display:none}
.langs > summary:hover{color:var(--fg)}
.langs > summary span{letter-spacing:0;text-transform:none;font-weight:600;font-size:13.5px;
  color:var(--muted);padding:3px 11px;border:1px solid var(--line);border-radius:999px;
  background:var(--raise)}
.langs > summary::after{content:"+";margin-left:2px;font-size:15px;line-height:1;
  letter-spacing:0;color:var(--dim)}
.langs[open] > summary::after{content:"−"}
.langs ul{display:flex;flex-wrap:wrap;gap:7px;margin:var(--s3) 0 0;padding:0;list-style:none}
.langs a{display:inline-block;padding:5px 13px;border:1px solid var(--line);border-radius:999px;
  font-size:13.5px;color:var(--muted);background:var(--raise);box-shadow:var(--inner);
  transition:color .15s var(--ease),border-color .15s var(--ease),transform .18s var(--spring)}
.langs a:hover{color:var(--fg);border-color:color-mix(in srgb,var(--fg) 24%,transparent);
  transform:translateY(-1px)}
/* The one you are reading is marked, and in white rather than red: this is
   "where you are", not an alarm. */
.langs a[aria-current]{color:var(--fg);background:var(--card);
  border-color:color-mix(in srgb,var(--fg) 42%,transparent)}
.langs p{margin:12px 0 0;color:var(--dim);font-size:12.5px}

footer .build{grid-column:1/-1;margin:var(--s4) 0 0;color:var(--dim);font-size:12.5px}
footer .build code{font-size:12px;padding:1px 6px}
/*
 * The sponsor line, when one is set (lib/sponsor.ts): the last thing on the
 * page, under the build line, in the muted grey rather than the dim one
 * because a disclosure is read, not glanced at (dim was 3.20:1 on the card
 * when this was written; it is 4.59:1 now, and muted is 5.53:1).
 * min-width 0 and anywhere-wrapping because the text is sixty characters
 * somebody else chose, with no promise of a space in them — a flex or grid
 * item never shrinks below its longest word unless told it may.
 */
footer .sponsor{grid-column:1/-1;margin:var(--s2) 0 0;color:var(--muted);font-size:12.5px;
  min-width:0;overflow-wrap:anywhere}
footer .sponsor a{color:var(--muted);text-decoration:underline;text-underline-offset:2px}

/* ── dashboard ──────────────────────────────────────────────────────────── */
/*
 * The dashboard is an app rather than a page, so it gets a wider shell and a
 * sidebar; everything else on the site keeps the text column.
 *
 * No backticks anywhere in here. The stylesheet is one template literal, and a
 * backtick in a comment ends it -- recorded in CLAUDE.md, three times.
 */
.wrap.wide{width:min(1320px,100% - 48px)}

/* The account menu in the bar: a disclosure, so it needs no script. */
.acct{position:relative}
.acct > summary{list-style:none;display:flex;align-items:center;gap:9px;cursor:pointer;
  padding:5px 12px 5px 5px;border:1px solid var(--line);border-radius:999px;background:var(--raise);
  font-size:14px;font-weight:600;box-shadow:var(--inner);
  transition:border-color .15s var(--ease)}
.acct > summary::-webkit-details-marker{display:none}
.acct > summary:hover,.acct[open] > summary{border-color:color-mix(in srgb,var(--fg) 24%,transparent)}
.acctname{max-width:16ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.acctmenu{position:absolute;right:0;top:calc(100% + 8px);z-index:30;min-width:200px;padding:6px;
  display:grid;gap:2px;background:var(--card);border:1px solid var(--line);border-radius:12px;
  box-shadow:var(--lift-hi)}
.acctmenu a,.acctmenu button{display:block;width:100%;text-align:left;padding:9px 11px;border-radius:8px;
  font:inherit;font-size:14px;color:var(--muted);background:none;border:0;cursor:pointer}
.acctmenu a:hover,.acctmenu button:hover{background:var(--raise);color:var(--fg)}

/* The initial under every server icon, in one box so a list never shifts as
   icons load or fail. */
.gicon img{position:absolute;inset:0;width:100%;height:100%;border-radius:inherit;object-fit:cover}
.gicon{position:relative;width:40px;height:40px;border-radius:50%;flex:none;overflow:hidden;
  display:grid;place-items:center;background:linear-gradient(160deg,var(--line-soft),var(--raise));
  border:1px solid var(--line);color:var(--muted);font-weight:650;font-size:15px}
.gicon.xs{width:26px;height:26px;font-size:12px}
.gicon.lg{width:56px;height:56px;border-radius:16px;font-size:20px}
.gicon.xl{width:72px;height:72px;border-radius:20px;font-size:26px;color:var(--fg)}

/* ── the public leaderboard ────────────────────────────────────────────────
   Numbers and names, nothing coloured: the palette's grey and white, with red
   held back for destruction and alarm. The top three are not gold or green —
   their rank is white where every other is grey, and brightness is the whole
   of the hierarchy. */
.bhead{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;margin-bottom:var(--s4)}
.btext{min-width:0;flex:1 1 220px}
.bhead h1{font-size:clamp(24px,3.4vw,34px);margin:4px 0 0}
/* One set of columns for the whole board: every row is a subgrid of the list,
   so a rank that gains a digit widens the rank column for every row rather
   than pushing only its own picture and name sideways. A browser without
   subgrid keeps the first template and lays each row out on its own. */
.board{list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:auto 40px minmax(0,1fr) auto;column-gap:var(--s3);row-gap:6px}
.brow{grid-column:1/-1;display:grid;grid-template-columns:auto 40px minmax(0,1fr) auto;
  grid-template-columns:subgrid;align-items:center;column-gap:var(--s3);padding:10px var(--s3);
  background:var(--card);border:1px solid var(--line);border-radius:12px;position:relative;
  overflow:hidden}
.brank{font-variant-numeric:tabular-nums;font-weight:680;color:var(--dim);min-width:2.5ch;
  text-align:right}
.btop .brank{color:var(--fg)}
/* The way to the next level: a hairline along the foot of the row, out of the
   grid, clipped by the row's own corners. Grey, and white for the top three
   like their rank. */
.bbar{position:absolute;left:0;right:0;bottom:0;display:block;width:100%;height:2px;margin:0;
  border:0;appearance:none;-webkit-appearance:none;background:var(--line)}
.bbar::-webkit-progress-bar{background:var(--line)}
.bbar::-webkit-progress-value{background:var(--muted)}
.bbar::-moz-progress-bar{background:var(--muted)}
.btop .bbar::-webkit-progress-value{background:var(--fg)}
.btop .bbar::-moz-progress-bar{background:var(--fg)}
.bname{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}
.bstat{display:flex;flex-direction:column;align-items:flex-end;gap:2px;text-align:right;flex:none}
.blevel{font-weight:650;font-size:14px}
.bxp{color:var(--muted);font-size:12.5px;font-variant-numeric:tabular-nums}
.bempty{color:var(--muted)}
.bnote{color:var(--dim);font-size:13.5px;margin-top:var(--s3)}
.pager{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);
  margin-top:var(--s4)}
.pwhere{color:var(--dim);font-size:13.5px;font-variant-numeric:tabular-nums}
.bstate{max-width:34rem;margin-inline:auto;text-align:center}
.bstate .meta{justify-content:center}
/* A phone: the score moves under the name, which otherwise had nothing left
   beside a five-figure rank and an eight-figure XP — and the page count takes
   a line of its own above the two buttons, which no longer fit beside it. */
@media (max-width:480px){
  .board{grid-template-columns:auto 40px minmax(0,1fr)}
  .brow{grid-template-columns:auto 40px minmax(0,1fr);grid-template-columns:subgrid;row-gap:2px}
  .brank,.brow .gicon{grid-row:1/3}
  .bname{grid-column:3;grid-row:1}
  .bstat{grid-column:3;grid-row:2;flex-direction:row;flex-wrap:wrap;align-items:baseline;
    column-gap:var(--s2);row-gap:0;text-align:left}
  .pager{flex-wrap:wrap}
  .pwhere{order:-1;flex-basis:100%;text-align:center}
}

/* A fact about the person, not an action, so quieter than the button beside it. */
.pill.owner{border-color:transparent;background:var(--raise);color:var(--muted);box-shadow:var(--inner)}

/* A light button: the dashboard's primary action, in monochrome, because red is
   rationed to destruction and alarm and "manage" and "save" are neither. */
.btn.solid{background:var(--fg);color:var(--bg);border-color:transparent;
  box-shadow:inset 0 -1px 0 rgba(0,0,0,.15),0 8px 24px -14px rgba(255,255,255,.35)}
.btn.solid:hover{border-color:transparent;background:var(--hi)}

/* ── the server list ───────────────────────────────────────────────────── */
.dhead{display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap}
.dtext{min-width:0;flex:1 1 320px}
.dtext h1{font-size:clamp(26px,3.8vw,38px);margin-top:6px}
.dtext .sub{margin-top:8px;color:var(--muted)}
.dnote{margin-top:6px;color:var(--dim);font-size:13.5px}

.servers{margin-top:var(--s5)}
.ggroup{margin-top:var(--s5)}
.servers > .ggroup:first-of-type{margin-top:var(--s4)}
.ghead{display:flex;align-items:center;gap:10px;margin:0;
  font-size:12.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--dim);font-weight:650}
.ghead::after{content:"";flex:1;height:1px;background:var(--line-soft)}
.ghead span{letter-spacing:0;font-size:12px;color:var(--muted);padding:1px 9px;
  border:1px solid var(--line);border-radius:999px;font-variant-numeric:tabular-nums}

/*
 * Cards in a grid of stated column counts -- never auto-fit, which resolves to
 * one column inside a container with no definite width (the footer taught
 * that). Three wide, two on a tablet, one on a phone.
 */
.ggrid{list-style:none;margin:var(--s3) 0 0;padding:0;display:grid;gap:12px;
  grid-template-columns:repeat(3,minmax(0,1fr))}
.gcard{position:relative;display:grid;grid-template-columns:auto minmax(0,1fr);
  grid-template-areas:"icon name" "icon meta" "act act";column-gap:14px;row-gap:4px;
  padding:16px;border:1px solid var(--line);border-radius:16px;
  background:linear-gradient(180deg,var(--card),var(--raise));box-shadow:var(--inner);
  transition:border-color .18s var(--ease),transform .2s var(--spring),box-shadow .18s var(--ease)}
.gcard:hover{border-color:color-mix(in srgb,var(--fg) 20%,transparent);transform:translateY(-2px);
  box-shadow:var(--inner),var(--lift)}
.gcard .gicon{grid-area:icon;align-self:center}
.gcard .gname{grid-area:name;align-self:end;font-weight:650;font-size:15.5px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.gcard .gmeta{grid-area:meta;align-self:start;display:flex;flex-wrap:wrap;gap:6px;align-items:center;
  min-height:22px}
.gstat{font-size:12.5px;color:var(--muted);font-variant-numeric:tabular-nums}
.gstat + .gstat::before{content:"·";margin-right:6px;color:var(--dim)}
.gcard .gact{grid-area:act;margin-top:12px;display:flex}
.gcard .gact .btn{flex:1}
/* A server without the bot reads one step quieter, so the ones you can manage
   are what the eye lands on. */
.gcard.away{background:var(--card)}
.gcard.away .gicon{opacity:.7}
.empty{margin-top:var(--s4)}

/* ── across the network ────────────────────────────────────────────────── */
/* Named with its parent so it outranks the section heading rule, which would
   otherwise set it at page-title size. */
.section > .nhead{margin:0;font-size:12.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--dim);font-weight:650}
.nlist{list-style:none;margin:var(--s4) 0 0;padding:0;display:grid;gap:8px}
.nrow{display:flex;align-items:center;gap:var(--s3);padding:10px 14px;
  border:1px solid var(--line);border-radius:12px;background:var(--card)}
.nname{font-weight:600;min-width:0;flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
/* Tabular, so "3/8" and "10/12" do not shuffle the buttons beside them. */
.ncount{font-variant-numeric:tabular-nums;color:var(--muted);font-size:13px;flex:none}
.nact{display:flex;align-items:center;gap:8px;margin-left:auto;flex:none}
/* The account page's one field and its button, side by side while there is room. */
.redeemf{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-top:var(--s2)}
.redeemf .setctl{flex:1 1 240px;min-width:0}
/* The account page's servers, one row each with the date beside the name; the
   sentence under an Add or Claim button that says what pressing it costs. */
.slist{list-style:none;margin:var(--s2) 0 0;padding:0}
.slist li{display:flex;justify-content:space-between;gap:12px;padding:8px 0;min-width:0;
  border-top:1px solid var(--line)}
.slist li span{min-width:0;overflow-wrap:anywhere}
.slist .gone{color:var(--muted)}
.slist time{color:var(--muted);font-variant-numeric:tabular-nums;flex:none}
.hint{display:block;font-size:13px;color:var(--muted);margin-top:4px}
.claimf{margin-top:var(--s2)}
.nwhich{flex:0 1 auto;min-width:0;max-width:100%}
.nwhich ul{list-style:none;margin:8px 0 0;padding:0;display:grid;gap:4px;font-size:13px;
  color:var(--muted)}
.nwhich summary{cursor:pointer}
.nwhich li{min-width:0;overflow-wrap:anywhere}
.nstate{color:var(--dim)}

/* ── the app shell: a sidebar and the page ─────────────────────────────── */
.app{display:grid;grid-template-columns:268px minmax(0,1fr);gap:40px;align-items:start;
  padding-top:var(--s5)}
.side{position:sticky;top:84px;max-height:calc(100svh - 100px);overflow:auto;
  scrollbar-width:thin;padding-right:4px}
.sidenav > summary{display:none}
.sidebody{display:grid;gap:14px}
.sguild{display:flex;align-items:center;gap:12px;padding:12px;border:1px solid var(--line);
  border-radius:14px;background:var(--card);box-shadow:var(--inner)}
.sguildtext{min-width:0;display:grid}
.sguildtext b{font-size:14.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sguildtext a{font-size:12.5px;color:var(--muted)}
.sguildtext a:hover{color:var(--fg)}
.snav{display:grid;gap:2px}
.scat{display:grid;gap:2px;margin-top:10px}
.scath{margin:0 0 4px;padding:0 10px;font-size:11px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--dim);font-weight:650}
.slink{display:flex;align-items:center;gap:10px;padding:7px 10px;border-radius:9px;
  font-size:14px;color:var(--muted);transition:background .15s var(--ease),color .15s var(--ease)}
.slink:hover{background:var(--raise);color:var(--fg)}
.slink[aria-current="page"]{background:var(--raise);color:var(--fg);
  box-shadow:inset 2px 0 0 var(--fg)}
.sname{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.slink[hidden],.scat[hidden]{display:none}
/* The sidebar when the bot answered the page but not the feature list. */
.snone{margin:10px 10px 0;color:var(--muted);font-size:12.5px}
/* On is a filled dot, off an outline: readable without colour at all. */
.dot{width:8px;height:8px;border-radius:50%;flex:none;border:1.5px solid var(--dim)}
.dot.on{background:var(--fg);border-color:var(--fg);box-shadow:0 0 0 3px rgba(255,255,255,.08)}
.dot.ring{border-color:var(--muted);border-radius:3px}

.appmain{min-width:0}
.apphead{display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap}
.apptext{min-width:0;flex:1 1 300px}
.apptext h1{font-size:clamp(26px,3.6vw,36px);margin-top:4px}
.chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
.chip{display:inline-flex;align-items:baseline;gap:6px;padding:6px 12px;border-radius:999px;
  border:1px solid var(--line);background:var(--raise);font-size:13px;color:var(--muted)}
.chip b{color:var(--fg);font-size:14px;font-variant-numeric:tabular-nums}
.chip.link:hover{color:var(--fg);border-color:color-mix(in srgb,var(--fg) 24%,transparent)}
.apphint{margin-top:var(--s4);color:var(--muted)}

/* The server's numbers: a grid of tiles that are links, red only on a finding. */
.ov{margin-top:var(--s4)}
.ovgrid{list-style:none;margin:var(--s3) 0 0;padding:0;display:grid;gap:12px;
  grid-template-columns:repeat(auto-fill,minmax(min(100%,160px),1fr))}
.ovtile{display:flex;flex-direction:column;gap:2px;height:100%;padding:14px 16px;
  border:1px solid var(--line);border-radius:16px;background:var(--card);box-shadow:var(--inner);
  color:var(--fg);transition:border-color .18s var(--ease)}
a.ovtile:hover{border-color:color-mix(in srgb,var(--fg) 24%,transparent)}
.ovn{font-size:26px;line-height:1.1;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
.ovl{font-size:13.5px;font-weight:600}
.ovw{font-size:12.5px;color:var(--muted)}
.ovtile.find{border-color:color-mix(in srgb,var(--red) 45%,var(--line));box-shadow:inset 3px 0 0 var(--red)}
.ovquiet{margin-top:var(--s3)}

/* What the last press did. Grey when it worked; red only when something was
   refused, because that is a finding somebody has to go and fix. */
.notice,.prose .notice{margin-top:var(--s3);padding:12px 14px;border:1px solid var(--line);
  border-radius:12px;background:var(--card);color:var(--fg);font-size:14px}
.notice.bad{border-color:color-mix(in srgb,var(--red) 45%,var(--line));
  box-shadow:inset 3px 0 0 var(--red)}
.notice ul{list-style:none;margin:8px 0 0;padding:0;display:grid;gap:6px;font-size:13.5px}
/* A refusal and what became of the typing are two sentences; p has no margin
   anywhere, so without this they ran together as one block, and the settings
   notice puts its list of refusals between the two. */
.notice p+p,.notice ul+p{margin-top:var(--s1)}
.rbad{color:var(--fg)}
/* Red, mixed towards the foreground: the bare accent is 3.9:1 on a card, under
   AA for text; this is the same mix the delete button uses, at 5.3:1. */
.rbad b{color:color-mix(in srgb,var(--red) 72%,var(--fg))}

.mgroup{margin-top:var(--s5)}
.mgrid{margin-top:var(--s3);display:grid;gap:12px;grid-template-columns:repeat(3,minmax(0,1fr))}
.mcard{display:flex;flex-direction:column;gap:8px;padding:16px;border:1px solid var(--line);
  border-radius:16px;background:var(--card);box-shadow:var(--inner);scroll-margin-top:96px;
  transition:border-color .18s var(--ease),background .18s var(--ease)}
.mcard:hover{border-color:color-mix(in srgb,var(--fg) 18%,transparent)}
/* The card a switch press came back to: the redirect names it in the fragment. */
.mcard:target{border-color:color-mix(in srgb,var(--fg) 30%,var(--line))}
/* On reads brighter than off, in monochrome: a lit edge, not a colour. */
.mcard.on{background:linear-gradient(180deg,var(--lit),var(--card));
  border-color:color-mix(in srgb,var(--fg) 14%,var(--line))}
.mtop{display:flex;align-items:center;gap:12px}
.mtop h3{margin:0;flex:1;min-width:0;font-size:15.5px}
.mname{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mname:hover{text-decoration:underline;text-underline-offset:3px}
.mdesc{color:var(--muted);font-size:13.5px;display:-webkit-box;-webkit-line-clamp:3;
  -webkit-box-orient:vertical;overflow:hidden}
.mfoot{margin-top:auto;padding-top:6px;display:flex;align-items:center;gap:10px;font-size:12.5px}
.mstate{color:var(--dim);text-transform:uppercase;letter-spacing:.12em;font-size:11px;font-weight:650}
.mcard.on .mstate{color:var(--fg)}
.mneed{color:var(--muted);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mgo{margin-left:auto;color:var(--muted);flex:none}
.mgo::after{content:" →"}
.mgo:hover{color:var(--fg)}

/*
 * The switch: a real submit button, drawn as a toggle. White on, dark off --
 * the whole palette, and readable in grayscale because the knob moves.
 */
.swf{margin:0;flex:none}
.switch{position:relative;width:44px;height:26px;border-radius:999px;cursor:pointer;flex:none;
  border:1px solid var(--line);background:var(--track);padding:0;
  transition:background .2s var(--ease),border-color .2s var(--ease)}
.switch .knob{position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;
  background:var(--muted);transition:transform .22s var(--spring),background .2s var(--ease)}
.switch[aria-checked="true"]{background:var(--fg);border-color:var(--fg)}
.switch[aria-checked="true"] .knob{transform:translateX(18px);background:var(--bg)}
.switch:hover{border-color:color-mix(in srgb,var(--fg) 40%,transparent)}

/* ── one feature's page ────────────────────────────────────────────────── */
.crumbs{display:flex;flex-wrap:wrap;gap:8px;font-size:13px;color:var(--dim)}
/* Every crumb and every page heading holds a value somebody else chose — a
   server name, a command name, a phrase to watch for — and Discord allows a
   hundred characters with no space in them. The break-word form on body wraps
   such a run but does NOT reduce the element's min-content width, and a flex
   item defaults to min-width auto, so one unbreakable crumb was a fixed 833px
   box: the responder editor was that much wider than every viewport, in every
   language, and a long server name would have done the same to the settings
   page. The anywhere form is the one that shrinks min-content too. Found by
   bun run layout, which is what it is for. (No backticks in this comment: the
   stylesheet is a template literal and one would end it four lines up. That
   is written down in CLAUDE.md and it still happened again here.) */
.crumbs > *{min-width:0;overflow-wrap:anywhere}
.crumbs a{color:var(--muted)}
.crumbs a:hover{color:var(--fg)}
.crumbs [aria-current]{color:var(--fg)}
.modhead{margin-top:10px;display:flex;align-items:flex-start;gap:var(--s4);flex-wrap:wrap}
.modtext{flex:1 1 320px;min-width:0}
.modtext h1{font-size:clamp(26px,3.6vw,36px);overflow-wrap:anywhere}
.modtext .sub{margin-top:8px;color:var(--muted);max-width:62ch}
.modsum{margin:12px 0 0;padding:0;list-style:none;max-width:70ch;display:grid;gap:6px}
.modsum li{padding-left:12px;border-left:2px solid var(--dim);color:var(--fg);font-size:14px;line-height:1.5}
.modsw{display:flex;align-items:center;gap:12px;padding:10px 12px 10px 16px;
  border:1px solid var(--line);border-radius:14px;background:var(--card)}
.modstate{font-size:12px;letter-spacing:.14em;text-transform:uppercase;font-weight:650;color:var(--muted)}
.callout{margin-top:var(--s3);padding:14px 16px;border:1px dashed color-mix(in srgb,var(--fg) 25%,var(--line));
  border-radius:12px;font-size:14px}
.callout ul{margin:8px 0 0;padding-left:18px;display:grid;gap:4px;color:var(--muted)}

.setform{margin-top:var(--s4)}
/* How fresh the pickers are: a quiet line, and the button for right now.
   Busy is an attribute rather than a class, so no class exists only in the
   script. */
.pickbar{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;
  margin-top:var(--s3);color:var(--dim);font-size:12.5px}
.pickstat{margin:0;min-width:0}
.pickbar[aria-busy="true"] .btn{opacity:.55;pointer-events:none}
.panel{margin-top:var(--s3);border:1px solid var(--line);border-radius:16px;background:var(--card);
  box-shadow:var(--inner);overflow:hidden}
/* A sentence, or a lone form, straight under a panel heading. The panel has no
   padding of its own because every row brings 18px; a bare child brought none
   and sat against the border. The row's inset, and room at the foot. */
.panel > :is(p,form){margin-inline:18px}
.panel > p{margin-top:14px}
.panel > :is(p,form):last-child{margin-bottom:16px}
.panh{display:flex;align-items:center;gap:10px;margin:0;padding:14px 18px;
  overflow-wrap:anywhere;min-width:0;
  font-size:12.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--dim);font-weight:650;
  border-bottom:1px solid var(--line-soft)}
.panh span{letter-spacing:0;font-size:12px;color:var(--muted);padding:1px 9px;
  border:1px solid var(--line);border-radius:999px}
details.panel > summary{cursor:pointer;list-style:none}
details.panel > summary::-webkit-details-marker{display:none}
details.panel > summary::after{content:"+";margin-left:auto;font-size:16px;letter-spacing:0;color:var(--muted)}
details.panel[open] > summary::after{content:"−"}
details.panel:not([open]) > summary{border-bottom:0}
/* Inside, not outside: the panel clips what overflows it, and a summary is
   flush with its edges, so the site's ring drawn 3px out was cut away on
   every side but the bottom. */
details.panel > summary:focus-visible{outline-offset:-3px}

/* A setup step: what it does on the left, the one press on the right. */
.step{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px 24px;align-items:center;
  padding:16px 18px;border-top:1px solid var(--line-soft)}
.panh + .step{border-top:0}
.step b{font-weight:600;font-size:15px}
.step p{margin-top:4px;color:var(--muted);font-size:13.5px}
/* Done, and still listed: the checklist is the record of what setup did.
   --muted, not --dim: --dim is 3.20:1 on --card, which is under AA for body
   text and, worse, dimmer than the description under this very title. Every
   other --dim on this page is small-caps metadata; a step's name is not. */
.step.sdid b{color:var(--muted)}
.stext{min-width:0}
.sact{display:flex;align-items:center;gap:10px;white-space:nowrap}
.sdone{font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);
  padding:2px 7px;border:1px solid var(--line);border-radius:999px;font-weight:650}
/* Where a step lives when only a person can do it.
   Two classes, because the step-paragraph rule outranks a bare .sin on every
   property it declares — the first version set a colour, a size and a margin
   and rendered none of them. Only the margin is worth having; the colour and
   size the paragraph rule already gives are the right ones.
   The inline-block on the chip is what stops it overflowing a narrow column: as
   a plain inline box with padding it stayed on the first line and ran 5.6px
   past the edge in French, Hungarian, Vietnamese and both Chinese variants at
   320px. Measured, not reasoned — 150 renders, five widths, every language. */
.step .sin{margin-top:7px}
.step .sin code{font-size:12px;padding:1px 5px;display:inline-block;max-width:100%}

/* A setting: what it is on the left, the control on the right. */
.srow{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,340px);gap:10px 32px;
  padding:16px 18px;border-top:1px solid var(--line-soft);scroll-margin-top:96px}
.panh + .srow{border-top:0}
.srow:target{background:var(--raise)}
.srow.bad{box-shadow:inset 3px 0 0 var(--red)}
.setlab{display:flex;align-items:center;flex-wrap:wrap;gap:8px;font-weight:600;font-size:15px}
.setdesc{margin-top:4px;color:var(--muted);font-size:13.5px}
.setnow{margin-top:8px;color:var(--dim);font-size:12.5px}
.setwhy{margin-top:8px;color:var(--fg);font-size:12.5px}
.req{font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);
  padding:2px 7px;border:1px solid var(--line);border-radius:999px;font-weight:650}
.sinfo{min-width:0}
.sctl{min-width:0;align-self:center}
/* The maker under a picker. Full width so it lines up with the control. */
.mkbtn{margin-top:8px;width:100%}

.setctl,.chipadd{display:block;width:100%;padding:9px 11px;border:1px solid var(--line);border-radius:10px;
  background:var(--raise);color:var(--fg);font:inherit;font-size:14px;
  transition:border-color .15s var(--ease)}
.setctl:hover,.chipadd:hover{border-color:color-mix(in srgb,var(--fg) 20%,transparent)}
/* No outline:none: the border carries pointer focus and the global focus-visible
   ring carries keyboard focus, which outline:none here used to delete. */
.setctl:focus,.chipadd:focus{border-color:color-mix(in srgb,var(--fg) 40%,transparent)}
/* A multi-select with the script blocked: tall enough to show several. */
.setctl.multi{min-height:9.5em}
textarea.setctl{resize:vertical;min-height:6em}
.withunit{display:flex;align-items:center;gap:10px}
.setctl.num{max-width:14ch}
.unit{color:var(--muted);font-size:13.5px}

/* A checkbox drawn as the same switch as the feature's own. */
.tog{appearance:none;-webkit-appearance:none;position:relative;width:44px;height:26px;margin:0;
  border-radius:999px;border:1px solid var(--line);background:var(--track);cursor:pointer;
  transition:background .2s var(--ease),border-color .2s var(--ease)}
.tog::after{content:"";position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;
  background:var(--muted);transition:transform .22s var(--spring),background .2s var(--ease)}
.tog:checked{background:var(--fg);border-color:var(--fg)}
.tog:checked::after{transform:translateX(18px);background:var(--bg)}

/* Two or three short choices, as one row of buttons. */
.seg{display:inline-flex;flex-wrap:wrap;gap:4px;padding:4px;border:1px solid var(--line);
  border-radius:12px;background:var(--raise)}
.segopt{position:relative;cursor:pointer}
.segopt input{position:absolute;opacity:0;width:1px;height:1px}
.segopt span{display:block;padding:6px 12px;border-radius:8px;font-size:13.5px;color:var(--muted);
  transition:background .15s var(--ease),color .15s var(--ease)}
.segopt input:checked + span{background:var(--fg);color:var(--bg);font-weight:600}
.segopt input:focus-visible + span{outline:2px solid var(--fg);outline-offset:2px}

/* Chips, built by the script out of a multi-select it then hides. */
.chipbox{display:grid;gap:8px}
.chiplist{display:flex;flex-wrap:wrap;gap:6px;min-height:0}
.chipx{display:inline-flex;align-items:center;gap:4px;padding:4px 4px 4px 11px;border-radius:999px;
  border:1px solid var(--line);background:var(--raise);font-size:13px;
  min-width:0;max-width:100%;overflow-wrap:anywhere}
.chipx button{width:22px;height:22px;border-radius:50%;border:0;background:none;color:var(--muted);
  cursor:pointer;font:inherit;font-size:15px;line-height:1}
.chipx button:hover{background:var(--line);color:var(--fg)}
.chipbox select[hidden]{display:none}

/* ── the message editor ──────────────────────────────────────────────────
   One greeting: its preview, the variable list, and a row per field and per
   button. Almost everything reuses the settings page's own srow/sctl/tog, so
   what is here is the preview and the two things a repeated row needs. */

/* A numbered slot — field three, button two — wrapping the rows that make it
   up, so the number is attached to its own group rather than to a heading
   that would be eleven headings deep. */
.mrow{padding:4px 0 10px;border-top:1px solid var(--line-soft)}
.mrow:first-of-type{border-top:0}
.mnum{display:block;padding:10px 18px 0;color:var(--dim);font-size:12px;
  letter-spacing:.14em;font-weight:650}
.mrow .srow{border-top:0;padding-top:8px;padding-bottom:8px}

/* The colour: a switch that says whether one was chosen, and the picker. An
   input[type=color] always has a value, so without the switch beside it the
   default would read as somebody's decision. */
.colpick{display:flex;align-items:center;gap:12px}
.setctl.col{width:52px;height:34px;padding:2px;cursor:pointer;flex:0 0 auto}

/* The preview: what Discord will be sent. Not a Discord skin — the point is
   that the words and their order are right, so it borrows this site's own
   surface and only the embed's accent comes from the message. */
.mprev{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;padding:16px 18px}
.mpc{white-space:pre-wrap;overflow-wrap:anywhere;font-size:14.5px;line-height:1.6}
.mpe{display:grid;gap:7px;padding:12px 14px;border-radius:10px;background:var(--raise);
  box-shadow:inset 3px 0 0 var(--mpc,var(--line))}
.mpa{color:var(--muted);font-size:13px;font-weight:600;min-width:0;overflow-wrap:anywhere}
.mpt{font-weight:650;font-size:15px;min-width:0;overflow-wrap:anywhere}
.mpd{white-space:pre-wrap;overflow-wrap:anywhere;color:var(--fg);font-size:14px;line-height:1.6}
.mpf{display:flex;flex-wrap:wrap;gap:10px 22px;margin-top:2px}
.mpfd{min-width:0;flex:1 1 100%}
.mpfd.mpfi{flex:1 1 30%}
.mpfd b{display:block;font-size:13px;overflow-wrap:anywhere}
.mpfd span{display:block;color:var(--muted);font-size:13.5px;white-space:pre-wrap;
  overflow-wrap:anywhere}
.mpft{color:var(--muted);font-size:12.5px;min-width:0;overflow-wrap:anywhere}
/* An address, never an image: this site sends img-src 'self', so loading a
   server's own CDN link would be refused by our own header and draw an empty
   box — which the dashboard shipped once with Discord's avatars. */
.mpurl{font-size:12.5px;overflow-wrap:anywhere}
.mpurl span{color:var(--dim)}
.mpnote{color:var(--dim);font-size:12px}
.mpb{display:flex;flex-wrap:wrap;gap:8px}
/* A button label is the server's and may be eighty characters with no space;
   a site button never wraps, and this one would have widened the page. */
.mpb .btn{white-space:normal;overflow-wrap:anywhere;max-width:100%;text-align:center}

/* The variable list, and the button the script puts under each box. */
.mvars > summary{cursor:pointer}
.mvars .vars{margin:8px 18px 18px}
.mvars h3{margin:14px 18px 0;font-size:13px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted)}
/* Clickable only once the inserter is wired, so with this file blocked the
   rows are a reference list and look like one. */
.js .mvars .var{cursor:pointer}
.js .mvars .var:hover{background:var(--raise)}
.varbtn{margin-top:8px;padding:5px 10px;border:1px solid var(--line);border-radius:999px;
  background:none;color:var(--muted);font:inherit;font-size:12.5px;cursor:pointer;
  transition:border-color .15s var(--ease),color .15s var(--ease)}
.varbtn:hover{border-color:color-mix(in srgb,var(--fg) 25%,transparent);color:var(--fg)}

/* The add form's own button, always there: an add is not a change to keep or
   reset, so it does not wait behind the save bar until something is typed. */
.addbar{display:flex;justify-content:flex-end;padding:4px 18px 16px}

/* The save bar: sticky at the bottom of the form. Always there without the
   script; with it, it waits until something has actually changed. */
.savebar{position:sticky;bottom:16px;z-index:10;margin-top:var(--s4);display:flex;align-items:center;
  gap:10px;flex-wrap:wrap;padding:10px 12px 10px 18px;border:1px solid var(--line);border-radius:14px;
  background:color-mix(in srgb,var(--card) 88%,transparent);box-shadow:var(--lift-hi);
  transition:opacity .2s var(--ease),transform .25s var(--spring)}
@supports (backdrop-filter:blur(1px)){.savebar{backdrop-filter:blur(14px)}}
.savemsg{margin-right:auto;font-size:14px;color:var(--muted)}
.js .savemsg{color:var(--fg)}
.savebar:not(.dirty) .savemsg{visibility:hidden}
/* Hidden, not only transparent: an opacity-only hide left Reset and Save in the
   tab order with an invisible focus ring. The visibility change waits for the
   fade on the way out and is immediate on the way in. */
.js .savebar:not(.dirty){opacity:0;transform:translateY(16px);pointer-events:none;visibility:hidden;
  transition:opacity .2s var(--ease),transform .25s var(--spring),visibility 0s linear .25s}

/* Windows High Contrast removes author backgrounds, and these controls said their
   state with nothing else: the system colours carry it instead. */
@media (forced-colors:active){
  .switch,.tog,.dot,.mockswitch{forced-color-adjust:none;border-color:ButtonText;background:Canvas}
  .switch .knob,.tog::after,.mockswitch::after{background:ButtonText}
  .switch[aria-checked="true"],.tog:checked,.mockswitch.on,.dot.on{background:Highlight;border-color:Highlight}
  .switch[aria-checked="true"] .knob,.tog:checked::after,.mockswitch.on::after{background:HighlightText}
  .segopt input:checked + span{forced-color-adjust:none;background:Highlight;color:HighlightText}
}
/* With the sidebar beside it the page is 545px wide at 901px, and the label
   column was 135px of that; a narrower control column gives the words room. */
@media (max-width:1000px) and (min-width:901px){
  .srow{grid-template-columns:minmax(0,1fr) minmax(0,260px)}
}
@media (max-width:1024px){
  .ggrid,.mgrid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:900px){
  /* One column. The sidebar becomes a disclosure above the page, which the
     script closes on load so the page starts at its content. */
  .app{grid-template-columns:minmax(0,1fr);gap:var(--s4);padding-top:var(--s4)}
  .side{position:static;max-height:none;overflow:visible;padding-right:0}
  .sidenav{border:1px solid var(--line);border-radius:14px;background:var(--card)}
  .sidenav > summary{display:flex;align-items:center;gap:10px;padding:10px 14px;cursor:pointer;
    list-style:none;font-weight:600}
  .sidenav > summary::-webkit-details-marker{display:none}
  .sidesumname{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .sidehint{font-size:12.5px;color:var(--muted)}
  .sidehint::after{content:" +"}
  .sidenav[open] .sidehint::after{content:" −"}
  .sidenav[open] > .sidebody{padding:0 10px 12px}
  .sidenav .sguild{display:none}
}
@media (max-width:720px){
  .srow{grid-template-columns:minmax(0,1fr)}
}
@media (max-width:640px){
  .ggrid,.mgrid{grid-template-columns:minmax(0,1fr)}
  .modsw{width:100%;justify-content:space-between}
}
@media (max-width:480px){
  /* The two network buttons cannot share a line with the name. */
  .nrow{flex-wrap:wrap;row-gap:10px}
  .nname{width:100%}
  .nact{margin-left:0}
  .acctname{display:none}
  .acct > summary{padding:4px}
  /*
   * With the name gone the toggle is a 34px circle, second in the bar, and a
   * 200px menu hung from ITS right edge ran up to 69px off the left of a phone
   * (2026-10-16). A leftward overflow adds nothing to scrollWidth, which is
   * why no width check saw it; the layout check now opens the menu and
   * measures both edges. Hung from the bar's own right edge instead, where the
   * folded link menu already drops from, it cannot leave the screen.
   */
  .nav .wrap{position:relative}
  .acct{position:static}
}

/* ── the landing page's picture of the dashboard ───────────────────────── */
/*
 * Two columns: the steps, and a drawing of the dashboard beside them. The
 * drawing is spans and the same dot and card vocabulary the real dashboard
 * uses, so it looks like the product because it is made of the product's
 * parts.
 */
.showcase{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);gap:var(--s5);
  align-items:center}
.showtext > h2{font-size:clamp(25px,3.6vw,34px);margin-top:var(--s2)}
.showtext > .sub{margin-top:var(--s2);color:var(--muted)}
.show{display:grid;grid-template-columns:150px minmax(0,1fr);gap:14px;padding:14px;
  border:1px solid var(--line);border-radius:20px;
  background:linear-gradient(180deg,var(--card),var(--bg));box-shadow:var(--inner),var(--lift-hi);
  transform:perspective(1400px) rotateY(-4deg);transform-origin:left center}
.showside{display:grid;gap:4px;align-content:start;font-size:12.5px;color:var(--muted)}
.showguild{display:flex;align-items:center;gap:8px;padding:6px;margin-bottom:6px;
  border:1px solid var(--line);border-radius:10px;background:var(--card);color:var(--fg);
  overflow:hidden;white-space:nowrap}
.showlink{display:flex;align-items:center;gap:8px;padding:5px 7px;border-radius:7px;
  overflow:hidden;white-space:nowrap}
.showlink.cur{background:var(--raise);color:var(--fg)}
.showmain{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;align-content:start}
.showcard{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 10px;padding:12px;
  border:1px solid var(--line);border-radius:12px;background:var(--card);font-size:12.5px}
.showcard.on{border-color:color-mix(in srgb,var(--fg) 14%,var(--line));
  background:linear-gradient(180deg,var(--lit),var(--card))}
.showcard b{font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.showdesc{grid-column:1 / -1;color:var(--muted);display:-webkit-box;-webkit-line-clamp:2;
  -webkit-box-orient:vertical;overflow:hidden}
.mockswitch{position:relative;width:32px;height:19px;border-radius:999px;background:var(--track);
  border:1px solid var(--line)}
.mockswitch::after{content:"";position:absolute;top:2px;left:2px;width:13px;height:13px;
  border-radius:50%;background:var(--muted)}
.mockswitch.on{background:var(--fg);border-color:var(--fg)}
.mockswitch.on::after{left:15px;background:var(--bg)}
.chipadd:disabled{opacity:.55;cursor:not-allowed}
@media (max-width:1024px){
  .showcase{grid-template-columns:minmax(0,1fr)}
  .show{transform:none}
}
@media (max-width:560px){
  .show{grid-template-columns:minmax(0,1fr)}
  .showside{display:none}
  .showmain{grid-template-columns:minmax(0,1fr)}
}

/* ── status: the numbers and the graph ──────────────────────────────────── */
/*
 * A stat card leads with the number, because that is what somebody came for.
 * Tabular figures, so a percentage changing from 99.9 to 100 does not shuffle
 * the text beside it on every reload.
 */
.card.stat h2{font-size:clamp(26px,4vw,34px);letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.card.stat p{margin-top:6px}

/*
 * The graph. The stroke inherits the current colour, so the line's colour is
 * decided here and nowhere else — grey, because a latency graph is information
 * and not an alarm. Red is rationed to destruction, alarm and findings.
 */
.chart{margin:var(--s4) 0 0;padding:var(--s4);border:1px solid var(--line);border-radius:15px;
  background:var(--card);color:var(--muted)}
.chart .spark{display:block;width:100%;height:64px;overflow:visible}
/* The viewBox is stretched to the box, so without this a flat run draws at 2px
   and a steep one at nearly 7 — the shape would no longer be the data's. */
.chart .spark path{vector-effect:non-scaling-stroke}
.chart figcaption{margin-top:12px;color:var(--dim);font-size:13px;line-height:1.5}

/* ── what the script builds ─────────────────────────────────────────────── */
/*
 * Every rule in this block styles an element that does not exist in the served
 * markup: web/script.ts creates all of them. That is deliberate and it is what
 * makes the enhancement honest — with the script blocked there is no half-built
 * control sitting on the page doing nothing.
 *
 * tests/web.test.ts reads the class names straight out of the script rather
 * than from a list somebody maintains beside it, so a rule here with no element
 * and an element with no rule are both build failures.
 */
.search{position:relative;display:flex;align-items:center;margin:var(--s4) 0 0}
.sinput{flex:1;min-width:0;appearance:none;font:inherit;font-size:15px;color:var(--fg);
  background:var(--card);border:1px solid var(--line);border-radius:11px;
  padding:11px 46px 11px 15px;box-shadow:var(--inner);
  transition:border-color .18s var(--ease),background .18s var(--ease)}
.sinput::placeholder{color:var(--dim)}
/*
 * No outline:none here, and no ring of its own.
 *
 * The global :where(...):focus-visible rule already draws the red one, and
 * :where() has zero specificity — so any outline:none on this class would win
 * and quietly delete the keyboard focus indicator on the only field on the
 * site. The border and the ground carry pointer focus; the outline carries
 * keyboard focus; red stays rationed to one of them.
 */
.sinput:focus{border-color:color-mix(in srgb,var(--fg) 30%,transparent);background:var(--raise)}
/* The hint is a label for a key, so it stands down the moment the field has
   focus and pressing that key would type into it instead. */
.skey{position:absolute;right:13px;pointer-events:none;color:var(--dim);font-size:12px;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;background:var(--bg);
  border:1px solid var(--line);border-radius:6px;padding:1px 7px;
  transition:opacity .15s var(--ease)}
.sinput:focus ~ .skey{opacity:0}
.scount{margin:var(--s2) 0 0;color:var(--muted);font-size:13.5px}

/*
 * A search overrides the chips, and it has to: a match in a category the chips
 * have filtered out still needs to be reachable, or the count above the list is
 * describing rows nobody can see.
 */
.cmds[data-q] .cat{display:block}
.cmds[data-q] .cat[hidden]{display:none}
/*
 * hidden is a user-agent rule and every one of these sets its own display in
 * this sheet, so an author rule beats it whatever the specificity and the
 * attribute would quietly do nothing. Said here once, for all of them.
 */
.cmd[hidden],.gcard[hidden],.ggroup[hidden]{display:none}

/*
 * copybtn, not copy — and the name is the whole story.
 *
 * The first version of this rule was called .copy, which is also the hero's own
 * div.copy. So opacity:0 landed on the wordmark, the tagline, the buttons and
 * the counters, and the top of the landing page rendered as an empty red field
 * with an avatar in it. Every check passed: the class was used and it was
 * styled, which is all the coverage test asks. It was found by opening the page
 * and looking at it.
 */
.copybtn{opacity:0;font:inherit;font-size:12px;color:var(--dim);background:var(--raise);
  border:1px solid var(--line);border-radius:7px;padding:2px 8px;cursor:pointer;
  transition:opacity .15s var(--ease),color .15s var(--ease)}
.cmd:hover .copybtn,.var:hover .copybtn,.copybtn:focus-visible{opacity:1}
.copybtn:hover{color:var(--fg)}

/* The variables reference: the name to copy, what it is, and what it becomes. */
.vars{display:grid;margin:8px 0 28px;border:1px solid var(--line);border-radius:12px;
  overflow:hidden}
#variables h3{margin:28px 0 0}
/* Wide enough for the longest name and its copy button on one line. */
.var{display:grid;grid-template-columns:minmax(150px,270px) 1fr minmax(90px,170px);gap:14px;
  align-items:center;padding:10px 14px;background:var(--card)}
.var+.var{border-top:1px solid var(--line-soft)}
.vname{color:var(--fg);display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.vdesc{color:var(--muted);font-size:14px;line-height:1.5}
.vex{color:var(--dim);font-size:13px;overflow-wrap:anywhere}
@media (max-width:640px){
  .var{grid-template-columns:1fr;gap:4px}
}

.totop{position:fixed;right:18px;bottom:18px;z-index:25;font:inherit;font-size:13px;
  font-weight:600;color:var(--muted);cursor:pointer;
  background:color-mix(in srgb,var(--card) 92%,transparent);
  border:1px solid var(--line);border-radius:999px;padding:9px 16px;box-shadow:var(--lift);
  transition:color .15s var(--ease),transform .2s var(--spring)}
.totop:hover{color:var(--fg);transform:translateY(-2px)}
@supports (backdrop-filter:blur(1px)){.totop{backdrop-filter:blur(12px)}}
/* Above the save bar on the pages that have one. The pill appears at the same
   scroll depth a long form becomes dirty at, and it sat on the Save button. */
body:has(.savebar) .totop{bottom:96px}

/*
 * The menu is nothing at all until the bar is too narrow to hold the links.
 * display:contents means that at every other width the markup lays out exactly
 * as it did before there was a script.
 */
.nav .menu{display:contents}
.menu-btn{display:none}

/* The reveal, for the browsers with no scroll-driven animations — Firefox and
   Safari today. Where animation-timeline works the script stands down and the
   rule below this block does the same job with no observer. */
.reveal{opacity:.32;transform:translateY(16px)}
.reveal.shown{opacity:1;transform:none;
  transition:opacity .5s var(--ease),transform .5s var(--ease)}
/* A card still lifts under the pointer while it arrives. .reveal.shown is the
   same weight as .card:hover and comes later, so its transform:none won. The
   script takes both classes off once the arrival is over, which gives the card
   its own spring back; these cover the half-second before that. */
.card.reveal.shown:hover{transform:translateY(-3px)}
.gcard.reveal.shown:hover{transform:translateY(-2px)}

/* ── motion, only where it explains something ───────────────────────────── */
@view-transition{navigation:auto}
@keyframes rise{from{opacity:.4;transform:translateY(16px)}to{opacity:1;transform:none}}
/* backwards, not both: a fill that holds the last frame keeps its
   transform:none over the hover lift for as long as the page is open. */
@supports (animation-timeline:view()){
  .card,ol.steps li,.cmd,.qa{animation:rise linear backwards;animation-timeline:view();
    animation-range:entry 0% entry 42%}
}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation:none!important;transition:none!important}
  .btn:hover,.card:hover,.card.reveal.shown:hover,.gcard:hover,.gcard.reveal.shown:hover,
  .filters label:hover{transform:none}
}
@media (prefers-contrast:more){
  :root{--muted:#b8bbc1;--dim:#9b9ea4;--line:#3a3a41}
}

/* ── narrow ─────────────────────────────────────────────────────────────── */
@media (max-width:720px){
  :root{--shell:min(1120px,100% - 32px);--s7:64px;--s6:44px}
  .hero{padding:var(--s7) 0 56px}
  /* Copy first on a phone: a 176px picture above the fold pushes the sentence
     that explains the bot off the bottom of the screen. */
  .hero .wrap{flex-direction:column;align-items:flex-start}
  .mark{width:104px;height:104px;order:-1;margin-inline:0}
  main{padding:var(--s6) 0 var(--s7)}
  .nav a.link{padding:8px 9px}
  /*
   * Dividers only work on a single row. Once the four stats wrap, the rule
   * that separates the third from the fourth ends up hanging off the left of
   * the second row with nothing before it. A grid drops them and lines the
   * numbers up in a column instead, which is what the eye wants anyway.
   */
  .stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s3) var(--s4)}
  .stats span{padding-left:0;border-left:0}
  /* One column for the footer as a whole; the three link columns stay three,
     because they are short words and stacking them would make the footer as
     tall as the page. */
  footer .wrap{grid-template-columns:1fr;gap:var(--s4)}
  footer nav{gap:var(--s4) var(--s3)}
}
/*
 * Folded: the links and the Dashboard button move into a panel and the primary
 * action stays out, because that is the one thing somebody came to press.
 *
 * A class the script sets when the bar does not fit on one row, not a width.
 * The width used to be 640px, chosen in English — and at 641px every language
 * wrapped Add to Discord onto a second row on its own, German and Russian did
 * the same at 768px, and on the narrowest phones they wrapped even folded. In
 * thirty languages the labels are thirty different lengths, so the only width
 * that is right is the one the page measures (web/script.ts).
 */
.nav.fold .wrap{position:relative}
.nav.fold .menu-btn{display:inline-flex}
.nav.fold .menu{display:none;position:absolute;top:calc(100% + 8px);right:0;min-width:208px;
  padding:7px;background:var(--card);border:1px solid var(--line);border-radius:13px;
  box-shadow:var(--lift-hi);z-index:30}
.nav.fold.open .menu{display:block}
.nav.fold .menu a.link{display:block;padding:10px 12px;font-size:15px}
.nav.fold .menu .btn{width:100%;margin-top:5px;margin-left:0}
@media (max-width:640px){
  /*
   * The copy button is invisible until hover, and it still occupies 48px of
   * layout — a fifth of a command row on a phone, taken straight out of the
   * description, which then wrapped four words to a line. There is no hover on
   * a touch screen to reveal it with, and the text can be long-pressed, so the
   * space goes back to the sentence.
   */
  .copybtn{display:none}
}
@media (max-width:480px){
  /* Below this "Support server" and "Add to Discord" cannot sit three abreast
     without breaking every label onto two lines. */
  footer nav{grid-template-columns:repeat(2,minmax(0,1fr))}
  .nav .wrap{--nav-gap:2px}
  .brand{font-size:15px;gap:8px}
  .nav a.link{padding:8px 7px;font-size:13.5px}
  .btn.sm{padding:8px 11px;font-size:13.5px}
  .cta .btn{flex:1 1 auto}
}
/*
 * Below about 400px the four items are wider than the bar however far the type
 * comes down, so the bar becomes two rows rather than clipping the button off
 * the right edge. The zero-width ::after is the row break: it is a flex item of
 * the same container, ordered between the button and the links, and a
 * flex-basis of 100% forces everything after it onto a new line.
 *
 * Hiding the links would have been one declaration, but the footer is thousands
 * of pixels down on the command reference, and a phone with no navigation at
 * the top of that page is a dead end. A menu button is the usual answer, and
 * web/script.ts now builds one — so this is the layout a visitor with
 * scripting off gets, and it has to stay correct on its own.
 */
@media (max-width:400px){
  .nav .wrap{flex-wrap:wrap;height:auto;padding:9px 0;row-gap:3px}
  .brand{order:0}
  .btn.sm{order:1}
  .nav .wrap::after{content:"";order:2;flex-basis:100%;height:0}
  .nav a.link{order:3;padding:6px 8px}
  /* With the script running the links are in the panel, so the row break would
     only add an empty second row under a bar that already fits. */
  .js .nav .wrap::after{content:none}
}
/* Account security (docs/systems/account-security.md): the second step, the trust dialog, the centre. */
.wrap.narrow{width:min(560px,100% - 32px)}
.secform{display:grid;gap:8px;margin-top:var(--s2)}
.secform .btn{justify-self:start}
.secform.inline,.inline{display:inline-block;margin-top:var(--s2)}
.seccard{padding:var(--s4)}
.seccard .secform{margin-top:var(--s3)}
.seclist{list-style:none;margin:var(--s2) 0 0;padding:0;display:grid;gap:10px}
.secrow{display:flex;gap:12px;align-items:center;justify-content:space-between;padding:10px 0;border-top:1px solid var(--line)}
.secrow:first-child{border-top:0;padding-top:0}
.secrow>div{min-width:0;display:grid;gap:2px}
.secrow .muted{color:var(--muted);font-size:14px;overflow-wrap:anywhere}
.secrow .hint{margin-top:0}
.secrow{flex-wrap:wrap}
.secrow>div{flex:1 1 12rem;overflow-wrap:anywhere}
.card .btn{white-space:normal;max-width:100%;text-align:center;overflow-wrap:anywhere}
.secadd{display:grid;gap:12px;margin-top:var(--s3)}
.secother{margin-top:var(--s3);font-size:14px}
.secother summary{cursor:pointer;color:var(--muted)}
.secother ul{list-style:none;margin:8px 0 0;padding:0;display:grid;gap:6px}
.codes{list-style:none;margin:var(--s3) 0;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:8px}
.codes code{display:block;text-align:center;padding:10px 8px;font-size:16px;letter-spacing:.06em;user-select:all}
.qr{display:flex;justify-content:center;margin:var(--s2) 0 var(--s3);background:#fff;border-radius:12px;padding:12px}
.qr svg{width:min(240px,100%);height:auto}
.secret{display:inline-block;letter-spacing:.08em;user-select:all;overflow-wrap:anywhere}
.alarm{color:color-mix(in srgb,var(--red) 80%,var(--fg))}
dialog.trust{position:static;margin:12vh auto 0;max-width:460px;width:100%;padding:var(--s4);border:1px solid var(--line);border-radius:16px;background:var(--card);color:var(--fg);box-shadow:var(--inner),var(--lift)}
dialog.trust h1{font-size:22px;margin-bottom:var(--s2)}
dialog.trust p{color:var(--muted);font-size:14.5px}
.trustf{display:flex;gap:10px;flex-wrap:wrap;margin-top:var(--s3)}
@media (max-width:480px){
  .secrow{flex-direction:column;align-items:stretch}
  .secrow .btn,.secrow form{justify-self:start}
}

/* ── template store ─────────────────────────────────────────────────────── */
.muted{color:var(--muted)}
.tplfilter{display:grid;grid-template-columns:minmax(0,2fr) repeat(3,minmax(0,1fr)) auto;gap:10px;margin-top:var(--s3);align-items:center}
.tplcard h3{font-size:18px;overflow-wrap:anywhere}
.tplcard h3 a:hover{text-decoration:underline}
.tplby{font-size:14px}
.tplsum{margin-top:8px;overflow-wrap:anywhere}
.tplrow{margin-top:10px}
.tplstars b{font-variant-numeric:tabular-nums}
.tplpages{display:flex;gap:14px;align-items:center;justify-content:center;margin-top:var(--s3)}
.tplapply code{display:inline-block;padding:6px 10px;border-radius:8px;background:var(--raise);border:1px solid var(--line);overflow-wrap:anywhere}
.tpltree{display:grid;gap:10px;margin-top:var(--s2)}
.tplcat summary{cursor:pointer;font-weight:650;letter-spacing:.04em;text-transform:uppercase;font-size:13px}
.tplcat ul{margin-top:6px}
.tplglyph{display:inline-block;width:1.2em;color:var(--muted);text-align:center}
.tplcomments{display:grid;gap:14px;margin-top:var(--s2)}
.tplcomment{padding:12px 14px;border:1px solid var(--line);border-radius:12px;overflow-wrap:anywhere}
.tplcomment.reply{margin-left:22px;border-style:dashed}
.tplbody{margin-top:6px;white-space:pre-wrap}
.tplactions{display:flex;flex-wrap:wrap;gap:10px;margin-top:10px;align-items:center}
.tplactions form{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.tplstarset{border:0;padding:0;margin:0}
.tplstarrow{display:flex;flex-direction:row-reverse;justify-content:flex-end;gap:6px}
.tplstar{cursor:pointer;font-size:26px;line-height:1;color:var(--muted)}
.tplstar input{position:absolute;opacity:0;width:1px;height:1px}
.tplstar:hover,.tplstar:hover ~ .tplstar,.tplstar:has(input:checked),.tplstar:has(input:checked) ~ .tplstar{color:var(--fg)}
.tplstar:has(input:focus-visible){outline:2px solid var(--fg);outline-offset:3px;border-radius:6px}
.tplfind li{color:var(--red)}
.tplnote{margin-top:var(--s2)}
.tplwho{font-size:14px}
.tplword{display:inline-flex;align-items:center;gap:4px;padding:4px 4px 4px 11px;border-radius:999px;border:1px solid var(--line);font-size:14px}
.tplword form{display:inline-flex}
.tplword button{width:22px;height:22px;border-radius:50%;border:0;background:none;color:var(--muted);cursor:pointer}
.tplword button:hover{background:var(--line);color:var(--fg)}
.tplband{margin-top:var(--s5)}
@media (max-width:720px){
  .tplfilter{grid-template-columns:minmax(0,1fr)}
  .tplcomment.reply{margin-left:10px}
}
