:root { color-scheme: dark; }

* { box-sizing: border-box; }
html { background: var(--background); scroll-behavior: smooth; }
body {
  margin: 0;
  /* Sticky footer, same as the app shell: the page reaches the viewport and the
     footer sits at the bottom of it, so a short page — a 404, a dead invite —
     does not leave the footer one scroll below the fold. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  /* Flat night tone, matching html — a vertical page gradient shows its tail
     as a band wherever the content stops short of the viewport. */
  background: var(--background);
  color: var(--text);
  font: var(--text-body);
  -webkit-font-smoothing: antialiased;
}
body > main { flex: 1 0 auto; }
body > .app-footer { flex: none; }
::selection { background: var(--brand); color: var(--background); }
/* No document-level ::-webkit-scrollbar skin here. Styling it turns the
   platform's overlay scrollbar into a classic 10px one that takes layout width,
   and this shell is the only one that did: every centered element — the header
   wordmark included — sat 5px left of where the same header sits on /dashboard
   or /trophies. A scrollable box inside a page may still style its own. */
a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 0.22em; }
button, a, input, select, textarea { -webkit-tap-highlight-color: transparent; }
:focus-visible { outline: var(--border-width-strong) solid var(--brand); outline-offset: 3px; }
.skip-link { position: fixed; top: -5rem; left: 1rem; z-index: 50; background: var(--brand); color: var(--background); padding: var(--space-5) var(--space-6); border-radius: var(--radius-control); }
.skip-link:focus { top: 1rem; }

/* Same frame as .shell on the app pages: one page width, panels a fixed gap
   apart, and no display-sized padding around the content. */
.page-shell { width: min(var(--page-content-max), calc(100% - var(--page-gutter) * 2)); margin: var(--space-5) auto var(--space-8); display: grid; gap: var(--space-6); align-content: start; }
.page-heading { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: var(--space-4) var(--space-7); }
.page-heading h1 { margin: 0; font: var(--text-page); }
.page-heading p { color: var(--muted); max-width: var(--measure); margin: var(--space-3) 0 0; }
.page-heading .heading-side { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-4); color: var(--muted); font: var(--text-meta); }

@keyframes live-signal { 50% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--brand) calc(var(--opacity-faint) * 100%), transparent); } }
.avatar { display: grid; place-items: center; background: var(--surface); color: var(--muted); overflow: hidden; }
.avatar img { width: 100%; height: 100%; object-fit: cover; }

.landing-section { display: grid; gap: var(--space-5); padding: var(--space-6); background: var(--surface); border: var(--border-width) solid var(--line); border-radius: var(--radius-surface); }
.landing-section > header { display: grid; gap: var(--space-2); }
.landing-section > .button, .landing-section > form > .button { justify-self: start; }
.landing-section h2 { margin: 0; font: var(--text-title); }
.landing-section header p { color: var(--muted); max-width: var(--measure); margin: 0; }
.step { padding: var(--space-7) var(--space-7) var(--space-7) 0; border-bottom: var(--border-width) solid var(--line); }
.step:nth-child(3n+2), .step:nth-child(3n+3) { padding-left: var(--space-7); border-left: var(--border-width) solid var(--line); }
.step strong { display: block; margin-bottom: var(--space-3); }
.step p { color: var(--muted); margin: 0; max-width: 36ch; font: var(--text-body); }
.semantic-comparison { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--line); border: var(--border-width) solid var(--line); }
.semantic-side { background: var(--surface); padding: var(--space-7); min-height: 15rem; }
.semantic-side h3 { margin: 0 0 var(--space-9); font: var(--text-item); }
.semantic-value { font: var(--text-display); font-variant-numeric: tabular-nums; }
.semantic-side p { max-width: 42ch; color: var(--muted); }
/* Cards, not a hairline table. The 1px-gap-over-a-light-background trick drew
   the grid's own colour through every cell a row was short of full, so seven
   members in a three-wide grid ended in a pale block — and the fixed three
   columns never collapsed, so on a phone the names ran off the right edge.
   auto-fill sizes the row to the members there are, on any width. */
.player-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: var(--space-6); }
.player-card { background: var(--surface); border: var(--border-width) solid var(--line); border-radius: var(--radius-surface); padding: var(--space-6); text-decoration: none; }

.avatar { width: 2.6rem; aspect-ratio: 1; border-radius: var(--radius-round); font-weight: var(--weight-bold); }
.player-card { display: grid; grid-template-columns: auto 1fr; gap: var(--space-5); align-content: start; }
.player-card strong { display: block; }
.player-card small { color: var(--muted); }
.data-semantics { color: var(--muted); }
.data-semantics { max-width: 80ch; margin: var(--space-6) 0 0; }
.chip { display: inline-flex; border: var(--border-width) solid var(--line); border-radius: var(--radius-pill); padding: var(--space-2) var(--space-4); font: var(--text-meta); color: var(--muted); }
.player-card > a { display: contents; text-decoration: none; }
/* A pushed console's local profiles: one row each, waiting to be claimed. */
.device-profile { display: flex; align-items: center; justify-content: space-between; gap: var(--space-6); min-height: 3.4rem; padding: var(--space-4) 0; border-bottom: var(--border-width) solid var(--line); }
.device-profile small { color: var(--muted); }
.device-profile button { min-height: 2.75rem; border: var(--border-width) solid var(--line); border-radius: var(--radius-control); padding: var(--space-4) var(--space-5); background: var(--background); color: var(--text); cursor: pointer; }

/* Inside the sign-in panel the gate is the panel's own content: a second frame
   around it read as a card inside a card. */
/* Wherever the gate already sits on a surface — the account panel, a roadmap
   section — it is that surface's content, not a second card drawn inside it. */

/* The account page is the product shell's one dense settings surface: a stack
   of panels, each panel one subject. */
.settings-heading { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: var(--space-5) var(--space-7); padding: var(--space-6); background: var(--surface); border: var(--border-width) solid var(--line); border-radius: var(--radius-surface); }
.settings-identity { display: flex; align-items: center; gap: var(--space-5); min-width: 0; }
.avatar.settings-avatar { width: 3.2rem; }
.settings-heading h1 { margin: 0; font: var(--text-page); }
.settings-identity-meta { margin: var(--space-2) 0 0; color: var(--muted); font: var(--text-body); overflow-wrap: anywhere; }
.settings-identity-meta a { color: var(--text); }

/* Signing in and naming a new profile are the account page's two one-panel
   states: the same surface as a settings section, sized for one column. */
.settings-panel { max-width: 46rem; padding: var(--space-6); background: var(--surface); border: var(--border-width) solid var(--line); border-radius: var(--radius-surface); }
.settings-panel h1 { margin: 0 0 var(--space-5); font: var(--text-page); }
.settings-panel .error-state { padding: 0 0 var(--space-5); }

.settings-stack { display: grid; gap: var(--space-6); align-content: start; }
/* Signing out ends the stack as a bare control, not a panel: it is one
   action, and a surface around it only advertised how little it holds. */
.settings-signout { display: flex; }
.settings-section { scroll-margin-top: 5rem; padding: var(--space-6); background: var(--surface); border: var(--border-width) solid var(--line); border-radius: var(--radius-surface); }
.settings-section h2 { margin: 0 0 var(--space-5); font: var(--text-title); }
/* Connecting a chat is three things that have to be true, so it is a list of
   three rows: what to do on the left, the control that does it on the right.
   The state line above says whether any of it is still needed. */
.community-telegram { display: grid; gap: var(--space-5); margin-bottom: var(--space-6); }
.community-telegram-state { margin: 0; color: var(--warning); font: var(--text-meta); }
.community-telegram-state.is-connected { color: var(--brand); }
.community-telegram-steps { display: grid; gap: var(--space-4); margin: 0; padding: 0; list-style: none; counter-reset: telegram-step; }
.community-telegram-steps li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-5);
  padding: var(--space-4) var(--space-5);
  border: var(--border-width) solid var(--line);
  border-radius: var(--radius-control);
  background: var(--background);
}
.community-telegram-steps li::before {
  counter-increment: telegram-step;
  content: counter(telegram-step);
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--background);
  font: var(--text-meta);
}
/* A done step keeps its place in the count but swaps the number for a tick,
   and its button stays only as the way back for switching the chat. */
.community-telegram-steps li.is-done::before { content: "✓"; }
.community-telegram-steps li > div { display: grid; gap: var(--space-1); min-width: 0; }
.community-telegram-steps strong { font: var(--text-item); }
.community-telegram-steps small { color: var(--muted); font: var(--text-micro); }
.community-telegram-steps code { color: var(--text); }
.community-telegram-steps li > .community-telegram-action { display: flex; align-items: center; gap: var(--space-4); }
.community-telegram-done { color: var(--brand); font: var(--text-meta); white-space: nowrap; }
@media (max-width: 560px) {
  .community-telegram-steps li { grid-template-columns: auto minmax(0, 1fr); }
  .community-telegram-steps li > :last-child { grid-column: 2; justify-self: start; }
  .community-telegram-steps li > .community-telegram-action { flex-wrap: wrap; }
}
/* The profile pickers are tiles, one per kind and per visibility: the glyph
   and the label the pills draw on the community page, so the admin chooses
   what readers will see. The radio itself is hidden; the tile carries the
   checked state and the focus ring in its place. */
.field-label { color: var(--muted); font: var(--text-meta); }
.community-choice-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); gap: var(--space-3); }
.community-choice { position: relative; display: block; cursor: pointer; }
.community-choice-grid .community-choice input {
  position: absolute;
  width: 1px;
  height: 1px;
  min-height: 0;
  padding: 0;
  margin: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}
.community-choice-body {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 2.7rem;
  padding: var(--space-3) var(--space-4);
  border: var(--border-width) solid var(--line);
  border-radius: var(--radius-control);
  background: var(--background);
  color: var(--muted);
  font: var(--text-meta);
  transition: color 0.15s ease, border-color 0.15s ease;
}
.community-choice:hover .community-choice-body { color: var(--text); }
.community-choice input:checked + .community-choice-body {
  border-color: var(--brand);
  color: var(--text);
  background: var(--raised);
}
.community-choice input:focus-visible + .community-choice-body {
  outline: var(--border-width-strong) solid var(--brand);
  outline-offset: 3px;
}
.community-choice-body .community-glyph { flex: none; width: 1.1rem; height: 1.1rem; }
/* An account-page section names itself on the page and holds its content on a
   surface below. The name outside the frame is what tells one section from the
   next down a long stack — sealed inside the panel it read as a caption of the
   panel instead, and sat at a different height than the sections whose content
   draws its own cards. */
.settings-block { scroll-margin-top: 5rem; display: grid; gap: var(--space-5); align-content: start; }
.settings-block > h2 { margin: 0; font: var(--text-title); }
.settings-block-body { padding: var(--space-6); background: var(--surface); border: var(--border-width) solid var(--line); border-radius: var(--radius-surface); }
/* A section whose content draws its own surfaces — the platform cards below —
   carries no panel: a bordered card inside a bordered panel framed one list
   twice. */
.settings-block-open > .settings-block-body { padding: 0; background: none; border: 0; border-radius: 0; }
/* The bot button wears Telegram's own mark and blue. */
.brand-glyph { width: 1.05em; height: 1.05em; margin-right: var(--space-3); fill: currentColor; }
.button-telegram { border-color: var(--telegram); background: var(--telegram); color: var(--text); }
.button-telegram:hover { background: var(--telegram-hover); border-color: var(--telegram-hover); }

/* Telegram is one connection, so its section opens like a platform card: the
   mark, the state of the link, and the actions that change it on one row. The
   page used to stack the state, an instruction and a button row as three grey
   paragraphs, which put the address — the one fact a reader came for — in the
   middle of the pile. */
.telegram-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  padding-bottom: var(--space-5);
  margin-bottom: var(--space-5);
  border-bottom: var(--border-width) solid var(--line);
}
/* Telegram's own mark, drawn as Telegram draws it: no tinted tile, no rounded
   plate of ours under it — the round logo is already a shape. */
.telegram-mark { flex: none; width: 1.75rem; height: 1.75rem; }
.telegram-identity { display: grid; gap: var(--space-1); min-width: 0; }
.telegram-state { color: var(--muted); font: var(--text-item); overflow-wrap: anywhere; }
.telegram-state.is-linked { color: var(--text); }
.telegram-state-note { color: var(--muted); font: var(--text-micro); }
.telegram-actions { display: flex; flex-wrap: wrap; gap: var(--space-4); margin-left: auto; }
/* Attaching Telegram is the section's own first step, so the widget sits under
   the row it belongs to and says nothing beside it: what it is for is the
   section's name, and where reports go is the line above. */
.telegram-connect { margin-bottom: var(--space-5); }
.telegram-note { margin: 0 0 var(--space-5); color: var(--muted); font: var(--text-meta); }
.telegram-note.is-warning { color: var(--warning); }
.settings-meta { margin: 0 0 var(--space-5); color: var(--muted); display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.settings-subhead { margin: var(--space-6) 0 var(--space-5); font: var(--text-item); }
.empty-note { margin: 0; color: var(--muted); }

.settings-form { display: grid; gap: var(--space-5); }
/* A group of controls that has nothing to act on — report delivery without a
   linked Telegram — stays readable and stops taking input, instead of
   pretending a saved setting will reach somebody. */
.settings-fieldset { display: grid; gap: var(--space-5); border: 0; margin: 0; padding: 0; min-width: 0; }
/* Half-opacity turned the off state into a smudge the reader had to lean into
   to see what they would be turning on. Muted keeps the rows legible; the head
   above already says why nothing here takes input yet. */
.settings-fieldset[disabled] { opacity: var(--opacity-muted); }
.form-actions { display: flex; flex-wrap: wrap; gap: var(--space-4); }
.check-stack { display: grid; gap: var(--space-5); }
/* The name of a report leads; the hashtag it carries in the chat follows it as
   a quiet tag, so the row can be read without knowing the filing scheme. */
.check-label { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-3); color: var(--text); }
.check-tag { color: var(--muted); font: var(--text-micro); }
/* "09:00" is four characters wide: half the form measure went to a field that
   never needed it, and the timezone next to it wrapped. */


/* One platform, one card. The blocks used to sit frameless in two columns of a
   panel: a short list beside a long one left a hole, and the rules that tried
   to hold the rows together drew a table. A card owns its own height, so a
   Steam block with two accounts beside a PSN block with four is simply shorter.
   Cards, not grid cells of one height — auto-fill sizes the row to the width. */
.platform-groups { display: grid; grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr)); gap: var(--space-6); }
.platform-group {
  min-width: 0;
  padding: var(--space-6);
  background: var(--surface);
  border: var(--border-width) solid var(--line);
  border-radius: var(--radius-surface);
}
/* The head names the platform and counts its accounts, and the one rule in the
   card separates it from them. Between the accounts there is only air. */
.platform-group-head { display: flex; align-items: center; gap: var(--space-4); padding-bottom: var(--space-5); border-bottom: var(--border-width) solid var(--line); }
/* The platform keeps its own identity mark, the same tinted chip the archive
   uses for a platform badge — a bare white logo on graphite read as a smudge. */
.platform-group-mark { display: grid; place-items: center; width: 2rem; height: 2rem; border-radius: var(--radius-control); border: var(--border-width) solid var(--line); background: var(--background); color: var(--muted); font: var(--text-meta); flex: none; }
.platform-group-mark img { max-width: 1.4rem; max-height: 1.4rem; object-fit: contain; }
.platform-group-mark.tone-psn { background: var(--platform-psn-bg); border-color: var(--platform-psn-border); }
.platform-group-mark.tone-steam { background: var(--platform-steam-bg); border-color: var(--platform-steam-border); }
.platform-group-mark.tone-nintendo { background: var(--platform-nintendo-bg); border-color: var(--platform-nintendo-border); }
.platform-group-mark.tone-xbox { background: var(--platform-xbox-bg); border-color: var(--platform-xbox-border); }
.platform-group-mark.tone-ps3 { background: var(--platform-ps3-bg); border-color: var(--platform-ps3-border); }
.platform-group-mark.tone-discord { background: var(--platform-discord-bg); border-color: var(--platform-discord-border); }
/* The name leads and the count sits under it, so the head reads as one thing
   however far a platform's name has to wrap in the card's width. */
.platform-group-name { display: grid; gap: var(--space-1); min-width: 0; }
.platform-group-name strong { font: var(--text-item); }
.platform-group-name small { color: var(--muted); font: var(--text-meta); }
/* The plus opens this platform's own add flow. It sits at the end of the head
   as a quiet outline: the card is a list of accounts, and the way to add one
   more belongs on the card, not only in a trigger at the foot of the page. */
.platform-group-add {
  flex: none;
  margin-left: auto;
  width: 1.75rem;
  height: 1.75rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: var(--border-width) solid var(--line);
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--text);
  font: var(--text-item);
  line-height: 1;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.platform-group-add:hover { color: var(--text); border-color: var(--brand); }
.platform-group-add:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.platform-group-add[aria-expanded="true"] { color: var(--text); border-color: var(--brand); }
/* A platform with nothing linked yet still keeps its card: the plus on it is
   the only way to connect one, so the card says the state instead of leaving a
   head over emptiness. */
.platform-group-empty { margin: var(--space-5) 0 0; color: var(--muted); font: var(--text-meta); }
.connection-rows { display: grid; gap: var(--space-5); margin: var(--space-5) 0 0; padding: 0; list-style: none; }
/* One account is one entry: the name, and under it when it last synced. The
   card is narrow, so the sync time sits under the handle instead of racing it
   to a right edge a long nickname would reach first. */
.connection-row { display: grid; gap: var(--space-1); min-width: 0; }
.connection-identity { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); min-width: 0; }
/* The account name is the row, not a heading: the platform above it keeps the
   heavier type, so a block of four handles stops shouting over its own title. */
.connection-handle { font: var(--text-body); font-weight: var(--weight-semibold); color: var(--text); overflow-wrap: anywhere; }
.connection-flag { line-height: 1; }
/* An integration keeps its account on another site, so its name is a link:
   underlined like the rest of the page's links, muted while hovered. */
/* An integration is one line, so its head carries the account instead of
   opening a list: nothing follows it, so it keeps no rule under it either. */
.platform-group-inline .platform-group-head { padding-bottom: 0; border-bottom: 0; }
.platform-group-inline .platform-group-name { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-4); }
.connection-handle-link { text-decoration-line: underline; }
.connection-handle-link:hover { color: var(--muted); }
.connection-meta { margin: var(--space-1) 0 0; color: var(--muted); font: var(--text-micro); }
/* Every row repeats the sync time, so it is the quietest line in the card. */
.connection-sync { margin: 0; color: var(--muted); font: var(--text-micro); }
/* Whatever waits on the person is set apart from the accounts that collect:
   a caption and a rule open the group, and each row is one shape — the state
   or the name, the step that is left under it, and the way forward or out on
   the right. The step is the only thing in the card asking for attention, so
   it is the only thing in the card in the warning colour. */
.connection-pending { margin-top: var(--space-6); padding-top: var(--space-5); border-top: var(--border-width) dashed var(--line); }
.connection-pending-caption { margin: 0; color: var(--muted); font: var(--text-micro); text-transform: uppercase; letter-spacing: 0.08em; }
.connection-pending .connection-rows { gap: var(--space-4); margin-top: var(--space-4); }
.connection-row-pending { grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--space-5); padding: var(--space-4) var(--space-5); border: var(--border-width) solid var(--line); border-radius: var(--radius-control); background: var(--background); }
.connection-pending-body { display: grid; gap: var(--space-2); min-width: 0; }
.connection-row-pending .connection-handle { color: var(--text); }
.connection-row-pending .connection-proof { margin: 0; color: var(--warning); }
.connection-pending-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-3); }

/* Every page that offers a sign-in already draws its own surface — the account
   panel, the roadmap section — so the choice adds no background and no frame of
   its own. A bordered box with a 1px grid gap inside those panels stacked three
   surfaces and read as a table. Two columns, one hairline, like `.step`. */
.auth-choice { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-6); max-width: 720px; }
.auth-choice > * + * { padding-left: var(--space-6); border-left: var(--border-width) solid var(--line); }
.auth-choice-single { grid-template-columns: minmax(0, 1fr); max-width: 22rem; }
.auth-provider { min-width: 0; }
.auth-provider h3 { margin: 0 0 var(--space-3); font: var(--text-item); }
.auth-provider p { color: var(--muted); min-height: 2.6rem; margin: 0 0 var(--space-6); }
.telegram-widget { min-height: 2.7rem; }
/* The dead copy of Telegram's Login Widget, drawn only where the widget itself
   refuses to mount. Its geometry is the widget's, not this design system's —
   the whole point is to see locally what the real pair of buttons measures. */
.telegram-stand-in {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  height: 40px;
  padding: 0 var(--space-6);
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--telegram);
  color: var(--text);
  font: var(--text-item);
}
.telegram-stand-in img { flex: none; }

.vote-control { display: flex; align-items: center; gap: var(--space-4); margin-top: var(--space-6); }
.vote-control button { width: 2rem; height: 2rem; border: var(--border-width) solid var(--line); background: var(--background); color: var(--text); border-radius: var(--radius-control); cursor: pointer; }
.vote-control output { min-width: 1.2rem; text-align: center; font-variant-numeric: tabular-nums; }
/* The status is a fact about the row, not its headline: a quiet pill beside the
   name, the way every other state in the product is drawn. */
.roadmap-status { display: inline-flex; align-items: center; padding: var(--pad-chip); border: var(--border-width) solid var(--line); border-radius: var(--radius-pill); color: var(--muted); font: var(--text-micro); text-transform: uppercase; letter-spacing: var(--tracking-label); }
.error-state { color: var(--danger); padding: var(--space-6) 0; }
.loading-line { color: var(--muted); padding: var(--space-8) 0; }
/* Dead ends — 404, a dead invite, a failed load — are ordinary pages: heading,
   one sentence, and a panel holding the way out. */
/* A quiet recovery screen within the shared navigation and footer. */
.message-shell { max-width: 36rem; margin-block: clamp(3rem, 14dvh, 9rem) var(--space-9); }
.message-panel { display: grid; justify-items: center; text-align: center; }
.message-mark { margin-bottom: var(--space-8); font: var(--text-display); color: var(--brand); font-variant-numeric: tabular-nums; }
.message-panel h1 { margin: 0; font: var(--text-page); text-wrap: balance; }
.message-detail { max-width: 42ch; margin: var(--space-5) 0 0; color: var(--muted); font: var(--text-body); text-wrap: pretty; }
.message-actions { justify-content: center; margin-top: var(--space-8); }
.message-actions .message-secondary { background: transparent; border-color: transparent; color: var(--muted); }
.message-actions .message-secondary:hover { background: var(--surface); color: var(--text); }
@media (max-width: 480px) {
  .message-shell { margin-top: var(--space-9); }
  .message-actions { flex-direction: column; width: 100%; max-width: 20rem; }
}

.toast { position: fixed; z-index: 40; right: 1rem; bottom: 1rem; max-width: 24rem; background: var(--background); border: var(--border-width) solid var(--line); border-radius: var(--radius-surface); padding: var(--space-5) var(--space-6); }

@media (max-width: 900px) {
  .landing-section > header, .page-heading { grid-template-columns: 1fr; }
}


@media (max-width: 560px) {
  .landing-section { padding: 4.5rem 0; }
  .landing-section > header { gap: var(--space-7); margin-bottom: var(--space-8); }
  .step, .step:nth-child(3n+2), .step:nth-child(3n+3) { padding: var(--space-6) 0; border-left: 0; }
  .page-heading .heading-side { align-items: flex-start; justify-content: flex-start; }
  .auth-choice { grid-template-columns: 1fr; }
  .auth-choice > * + * { padding: var(--space-6) 0 0; border-left: 0; border-top: var(--border-width) solid var(--line); }
  .settings-heading { align-items: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; transition-duration: .01ms !important; animation-duration: .01ms !important; }
}

/* The roadmap is the one service page left, and it is a list, not a data
   screen: it holds the reading width instead of stretching a five-row list
   across the 1380px the dashboard needs. */
.info-shell { width: min(var(--page-reading-max), calc(100% - var(--page-gutter) * 2)); }
/* Roadmap reads as a ranked list: the count carries the row, the feature
   explains itself, the controls sit at the end. */
.roadmap-list { display: grid; gap: 1px; background: var(--line); border: var(--border-width) solid var(--line); border-radius: var(--radius-surface); overflow: hidden; }
.roadmap-item { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr) auto; align-items: center; gap: var(--space-7); background: var(--surface); padding: var(--space-6); }
.roadmap-votes { display: grid; justify-items: center; gap: var(--space-1); color: var(--muted); }
.roadmap-votes strong { font: var(--text-page); color: var(--brand); font-variant-numeric: tabular-nums; }
/* Nothing voted for yet is not an achievement: an unvoted row keeps the count
   in ink, so the brand colour still means "people asked for this". */
.roadmap-item.is-unvoted .roadmap-votes strong { color: var(--muted); }
.roadmap-votes span { font: var(--text-micro); text-transform: uppercase; letter-spacing: var(--tracking-label); }
.roadmap-title { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.roadmap-body h2 { margin: 0; font: var(--text-item); }
.roadmap-body p { margin: var(--space-2) 0 0; color: var(--muted); font: var(--text-body); }
.roadmap-item .vote-control { margin-top: 0; }

@media (max-width: 700px) {
  .roadmap-item { grid-template-columns: 3.5rem minmax(0, 1fr); }
  .roadmap-item .vote-control { grid-column: 2; }
}

.community-settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-5);
  padding-block: var(--space-5);
  border-bottom: var(--border-width) solid var(--line);
}
.community-settings-row:last-child { border-bottom: 0; padding-bottom: 0; }
.community-settings-person {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  min-width: 0;
  text-decoration: none;
}
.community-settings-person .avatar { flex: 0 0 2.6rem; }
.community-settings-person > span:last-child { min-width: 0; overflow-wrap: anywhere; }
.community-settings-person strong, .community-settings-person small { display: block; }
.community-settings-person small { color: var(--muted); }
.community-settings-person .community-role-admin { color: var(--brand); font-weight: var(--weight-bold); }
.community-settings-person .member-telegram {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--warning);
}
.member-telegram-glyph { width: 1em; height: 1em; flex: 0 0 auto; fill: currentColor; }
#communityInviteOutput:not(:empty), #communityInviteList:not(:empty) { margin-top: var(--space-5); }
@media (max-width: 560px) {
  .community-settings-row > .button-row { flex-basis: 100%; }
}

/* Adding an account is a short errand inside a long page: the plus opens the
   platform's form over the list rather than pushing the cards below it apart,
   so the card you started from stays where you left it. The overlay is scoped
   to this page — the dashboard's own modal styles do not reach here. */
.source-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: clamp(1rem, 6vh, 4rem) var(--space-6);
  overflow-y: auto;
  /* The page stays legible behind the dialog: a heavier scrim turned the whole
     account into a black field and made the dialog look like a lone card. */
  background: var(--shade-3);
}
.source-overlay[hidden] { display: none; }
/* The page behind a dialog does not scroll: the errand is in front of it. */
body.modal-open { overflow: hidden; }
.source-dialog {
  width: 100%;
  max-width: 32rem;
  margin: auto;
  display: grid;
  gap: var(--space-5);
  padding: var(--space-6);
  background: var(--surface);
  border: var(--border-width) solid var(--line);
  border-radius: var(--radius-surface);
  box-shadow: var(--shadow);
}
.source-dialog:focus { outline: none; }
.source-dialog-head { display: flex; align-items: center; gap: var(--space-4); }
.source-dialog-title { display: flex; align-items: center; gap: var(--space-4); margin: 0; min-width: 0; font: var(--text-title); }
.source-dialog-title .platform-group-mark { flex: none; }
.source-dialog-close {
  flex: none;
  margin-left: auto;
  width: 2rem;
  height: 2rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: var(--border-width) solid var(--line);
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--text);
  font: var(--text-item);
  line-height: 1;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.source-dialog-close:hover { color: var(--text); border-color: var(--brand); }
.source-dialog-close:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
/* What the platform asks for, before the field that asks it: how ownership is
   proved, which setting has to be open, and where the source gives less than a
   person expects. It sits on the lifted surface so it reads as the platform
   talking, not as another paragraph of the page. */
.source-note {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-5);
  background: var(--raised);
  border-radius: var(--radius-control);
  color: var(--text);
  font: var(--text-body);
}
.source-note p { margin: 0; }
.source-note a { text-decoration-line: underline; }
.source-note-limits { color: var(--muted); }
.source-note-limits strong { color: var(--text); }
/* The dialog is one column at any width: its fields never race each other,
   and the body keeps the same seam between its parts that the dialog keeps
   between head and body -- the note and the form sat flush against each other
   before it, so the actions read as an edge of the note. */
.source-dialog-body { display: grid; gap: var(--space-5); align-content: start; }
.source-dialog-body .form-grid { grid-template-columns: 1fr; max-width: none; }
/* A secret that has not been issued yet is nothing, not an empty row. */
.source-dialog-body .connection-secret:empty { display: none; }
/* Cancel and the action close the dialog the way its head opened it: at the
   end, on the right, the action last. */
.source-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-4); }

/* A connection guide is a page of steps: the dialog states the platform's terms
   in three lines, and this is where the menus are actually named. */
.guide-tabs { display: flex; flex-wrap: wrap; gap: var(--space-4); }
.guide-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border: var(--border-width) solid var(--line);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--text);
  font: var(--text-meta);
  text-decoration: none;
}
.guide-tab:hover { border-color: var(--line); color: var(--text); }
/* The page you are on is a label, not a link you can follow again. */
.guide-tab[aria-current="page"] { border-color: var(--brand); color: var(--text); }
.guide-tab .platform-group-mark { width: 1.5rem; height: 1.5rem; }
.guide-tab .platform-group-mark img { max-width: 1rem; max-height: 1rem; }
.guide-steps { display: grid; gap: var(--space-6); }
/* One step, one card: a numbered heading and what to do under it. */
.guide-step {
  display: grid;
  gap: var(--space-5);
  padding: var(--space-6);
  background: var(--surface);
  border: var(--border-width) solid var(--line);
  border-radius: var(--radius-surface);
}
.guide-step h2 { display: flex; align-items: center; gap: var(--space-4); margin: 0; font: var(--text-title); }
.guide-step-number {
  flex: none;
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--radius-pill);
  background: var(--brand);
  color: var(--background);
  font: var(--text-meta);
}
.guide-step ul { display: grid; gap: var(--space-4); margin: 0; padding-left: var(--space-6); color: var(--text); font: var(--text-body); }
.guide-step li { padding-left: var(--space-2); }
/* Limits are the same card without a number: nothing here is a step to take. */
.guide-limits h2 { font: var(--text-item); }
.guide-limits ul { color: var(--muted); }

/* A connection waiting on the person says the one step that is left, and the
   account it has to be taken against. Louder than the sync line under it: this
   is the only row on the page that is asking for something. */
/* The step is a request, not a success, so it shares the waiting group's
   colour wherever it shows. */
.connection-proof { margin: var(--space-1) 0 0; color: var(--warning); font: var(--text-micro); }

/* How deeply an account is collected. Not a warning: "История" is a working
   connection on the first of two steps, so the badge sits in the muted register
   the sync line uses and leaves the warning colour to rows that are asking for
   something. */
.connection-tier {
  padding: 0 var(--space-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--muted);
  font: var(--text-micro);
  white-space: nowrap;
}
.connection-tier-hint { margin: var(--space-1) 0 0; color: var(--muted); font: var(--text-micro); text-wrap: pretty; }
.connection-tier-action {
  padding: 0;
  border: 0;
  background: none;
  color: var(--brand);
  font: inherit;
  cursor: pointer;
}
.connection-tier-action:hover { text-decoration: underline; }

/* Sign-in has one readable column; provider-owned buttons retain their branding. */
.signin-shell { max-width: 30rem; margin-top: clamp(2rem, 8vh, 5rem); }
.signin-panel { display: grid; justify-items: center; gap: var(--space-8); background: none; border: 0; box-shadow: none; text-align: center; }
.signin-panel .page-heading h1 { text-wrap: balance; }
.signin-panel p { text-wrap: pretty; }
.signin-panel .auth-choice { grid-template-columns: minmax(0, 1fr); gap: var(--space-6); }
.signin-panel .auth-choice > * + * { padding: 0; border: 0; }
.signin-panel .auth-provider h3 { display: none; }
.signin-panel .auth-provider { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); }
.signin-panel .auth-provider p { order: 1; min-height: 0; margin: 0; font: var(--text-meta); }
.signin-note { margin: 0; color: var(--muted); font: var(--text-meta); }
.telegram-head.is-unlinked { border-bottom: 0; padding-bottom: 0; }
.telegram-connect { margin-bottom: 0; }
.telegram-preferences { max-width: 60rem; }
.telegram-preference-group { min-width: 0; }
.telegram-preference-group h3 { margin: 0 0 var(--space-6); font: var(--text-item); }

.signin-logo { display: block; width: min(100%, 17rem); height: auto; }

/* Decorative covers stay behind the page's own content and never take input. */
.poster-scene { position: relative; isolation: isolate; overflow: hidden; min-height: 38rem; padding-block: var(--space-8); }
.poster-scene > .page-shell { position: relative; z-index: 1; }
/* Separate grid cells reserve room for each rotated poster and its drift. */
.poster-backdrop { position: absolute; inset: 0; container-type: size; display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); grid-template-rows: repeat(3, minmax(0, 1fr)); place-items: center; padding: 2rem 2%; pointer-events: none; mask-image: linear-gradient(to bottom, transparent, var(--black) 14%, var(--black) 78%, transparent); }
.poster-backdrop img { width: min(9vw, 11rem, calc((100cqh - 8rem) / 4.8)); height: auto; aspect-ratio: 3 / 4; object-fit: cover; border-radius: var(--radius-control); opacity: var(--opacity-light); transform: rotate(var(--poster-tilt)); animation: poster-drift 24s ease-in-out infinite alternate; animation-play-state: paused; }
.poster-backdrop.is-visible img { animation-play-state: running; }
.poster-backdrop img:nth-child(1) { grid-row: 1; grid-column: 1; --poster-tilt: -12deg; animation-delay: -3s; }
.poster-backdrop img:nth-child(2) { grid-row: 1; grid-column: 2; --poster-tilt: 9deg; animation-delay: -6s; }
.poster-backdrop img:nth-child(3) { grid-row: 2; grid-column: 1; --poster-tilt: 6deg; animation-delay: -9s; }
.poster-backdrop img:nth-child(4) { grid-row: 3; grid-column: 3; --poster-tilt: -8deg; animation-delay: -12s; }
.poster-backdrop img:nth-child(5) { grid-row: 1; grid-column: 8; --poster-tilt: 11deg; animation-delay: -15s; }
.poster-backdrop img:nth-child(6) { grid-row: 1; grid-column: 7; --poster-tilt: -7deg; animation-delay: -18s; }
.poster-backdrop img:nth-child(7) { grid-row: 2; grid-column: 8; --poster-tilt: -5deg; animation-delay: -21s; }
.poster-backdrop img:nth-child(8) { grid-row: 3; grid-column: 6; --poster-tilt: 10deg; animation-delay: -0s; }
.poster-backdrop img:nth-child(9) { grid-row: 3; grid-column: 1; --poster-tilt: 8deg; animation-delay: -3s; }
.poster-backdrop img:nth-child(10) { grid-row: 1; grid-column: 3; --poster-tilt: -13deg; animation-delay: -6s; }
.poster-backdrop img:nth-child(11) { grid-row: 3; grid-column: 2; --poster-tilt: 12deg; animation-delay: -9s; }
.poster-backdrop img:nth-child(12) { grid-row: 3; grid-column: 8; --poster-tilt: -10deg; animation-delay: -12s; }
.poster-backdrop img:nth-child(13) { grid-row: 2; grid-column: 7; --poster-tilt: 14deg; animation-delay: -15s; }
.poster-backdrop img:nth-child(14) { grid-row: 3; grid-column: 7; --poster-tilt: -6deg; animation-delay: -18s; }
.poster-backdrop img:nth-child(15) { grid-row: 2; grid-column: 2; --poster-tilt: 8deg; animation-delay: -21s; }
.poster-backdrop img:nth-child(16) { grid-row: 1; grid-column: 6; --poster-tilt: -9deg; animation-delay: -0s; }
@keyframes poster-drift {
  from { transform: translateY(0) rotate(var(--poster-tilt)); }
  to { transform: translateY(-1.5rem) rotate(var(--poster-tilt)); }
}
@media (max-width: 760px) {
  .poster-scene { min-height: 0; padding-block: 8rem 10rem; }
  .poster-scene > .page-shell { width: calc(100% - 3rem); margin-inline: auto; }
  .signin-panel { padding: 0; }
  .signin-logo { width: min(100%, 15rem); }
  .poster-backdrop { display: block; }
  .poster-backdrop img { position: absolute; width: min(6.5rem, 27vw); animation: none; opacity: var(--opacity-soft); }
  .poster-backdrop img:nth-child(even), .poster-backdrop img:nth-child(n+9) { display: none; }
  .poster-backdrop img:nth-child(1) { left: 7%; top: 0.5rem; }
  .poster-backdrop img:nth-child(5) { left: auto; right: 9%; top: 1rem; }
  .poster-backdrop img:nth-child(3) { left: 12%; top: auto; bottom: 1.5rem; }
  .poster-backdrop img:nth-child(7) { left: auto; right: 6%; top: auto; bottom: 0.75rem; }
}
@media (prefers-reduced-motion: reduce) {
  .poster-backdrop img { animation: none; }
}

/* Match the large Telegram button's pill silhouette on the sign-in screen. */
.signin-panel .google-button {
  width: 206px;
  min-width: 0;
  height: 40px;
  padding-inline: var(--space-6);
  border-radius: var(--radius-pill);
  font: var(--text-item);
  letter-spacing: normal;
}

.signin-panel .telegram-widget { width: max-content; max-width: 100%; }

.signin-panel .page-heading { justify-content: center; }
.signin-panel .signin-logo { margin-inline: auto; }
.signin-panel .signin-note { max-width: 36ch; }

/* Center a poster page's content within the shared site header and footer. */
body:has(.poster-stage) > main { display: flex; flex-direction: column; }
.poster-stage { flex: 1; display: grid; grid-template-rows: 1fr; }
/* The stage already fills the space between header and footer, so the scene
   takes that height instead of forcing a page taller than the viewport. */
.poster-stage .poster-scene { display: grid; align-items: center; min-height: 0; padding-block: 5rem 8rem; }
.poster-stage > .poster-scene > .page-shell { margin-block: 0; }
.signin-experience .signin-panel { padding: var(--space-7); gap: var(--space-7); }
.signin-experience .signin-note { max-width: 30ch; font: var(--text-meta); }
@media (max-width: 760px) {
  .poster-stage .poster-scene { min-height: 0; padding-block: 8rem 10rem; }
  .signin-experience .signin-panel { padding: 0; }
}

/* The sign-in hero already carries the full wordmark. */
body:has(.signin-experience) > .site-nav .site-wordmark { display: none; }

/* An invitation borrows the sign-in column — one centred stack over the
   drifting covers — with the community standing where the wordmark stood:
   eyebrow, mark, name, what it is, then the single action. */
.invite-shell { max-width: 26rem; }
.invite-panel { padding: var(--space-7); gap: var(--space-8); }
.invite-heading { display: grid; justify-items: center; gap: var(--space-4); }
.invite-eyebrow { margin: 0 0 var(--space-3); color: var(--muted); font: var(--text-micro); text-transform: uppercase; letter-spacing: var(--tracking-label); }
.avatar.invite-avatar { width: 5rem; border: var(--border-width) solid var(--line); font: var(--text-page); color: var(--brand); }
.invite-heading h1 { margin: 0; font: var(--text-display); font-weight: var(--weight-bold); letter-spacing: var(--tracking-label); text-wrap: balance; overflow-wrap: anywhere; }
/* The facts row is drawn here in full: product.css does not load the
   directory's card styles, and the glyph must not grow past its word. */
.invite-facts { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-3) var(--space-5); margin: var(--space-2) 0 0; padding: 0; list-style: none; color: var(--muted); font: var(--text-meta); }
.invite-facts li { display: inline-flex; align-items: center; gap: var(--space-2); }
.invite-facts .community-type { padding: var(--space-2) var(--space-4); border-radius: var(--radius-xs); color: var(--text); background: var(--raised); }
.invite-facts .community-glyph-wrap { display: inline-flex; }
.invite-facts .community-glyph { width: 1em; height: 1em; flex: none; }
.invite-description { max-width: 36ch; margin: var(--space-3) 0 0; color: var(--muted); font: var(--text-body); text-wrap: pretty; }
.invite-action { display: grid; justify-items: center; gap: var(--space-5); width: 100%; }
.invite-action .invite-note { max-width: 34ch; margin: 0; color: var(--muted); font: var(--text-meta); text-wrap: pretty; }
.invite-action .button { justify-content: center; }
.invite-join { width: 100%; max-width: 18rem; }
.invite-action .auth-choice { max-width: none; }
@media (max-width: 760px) {
  .invite-panel { padding: 0; }
  /* The eyebrow sits higher than the sign-in wordmark did; keep it clear of
     the top pair of covers. */
  .invite-experience .poster-scene { padding-block: 10rem; }
}

.signin-platforms { display: flex; justify-content: center; align-items: center; gap: var(--space-7); list-style: none; margin: var(--space-7) 0 0; padding: 0; }
.signin-platform-icon { display: block; width: 1.75rem; height: 1.75rem; background-color: var(--muted); mask: var(--platform-icon) center / contain no-repeat; }

.connection-info { position: relative; display: inline-flex; align-items: center; gap: var(--space-2); background: transparent; cursor: pointer; }
.connection-info:hover { color: var(--text); background: var(--raised); }
.connection-info:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }
.connection-info-panel { position: fixed; inset: auto; margin: 0; width: min(20rem, calc(100vw - 24px)); max-height: calc(100dvh - 24px); overflow: auto; color: var(--text); font: var(--text-body); }
.connection-info-panel strong { color: var(--muted); font: var(--text-micro); font-weight: var(--weight-bold); letter-spacing: var(--tracking-label); text-transform: uppercase; }
.connection-info-panel p { margin: var(--space-3) 0 0; text-wrap: pretty; }
@media (pointer: coarse) {
  .connection-info::after { content: ""; position: absolute; inset: 50% 0 auto; min-width: 44px; height: 44px; transform: translateY(-50%); }
  .connection-identity { min-height: 44px; }
}
