/*
 * Навстречу — the whole stylesheet.
 *
 * One file, no framework, and the two typefaces the design handoff specifies:
 * Golos Text for everything and IBM Plex Mono for invite codes and the character
 * counter. Both are served from this origin, because §15 forbids third-party
 * requests from participant pages — a font on somebody else's CDN would hand
 * that CDN a list of which addresses opened a note each morning for two weeks.
 * See `fonts/LICENCE.md` for what is bundled and why those subsets.
 *
 * Option 1c ("Daylight") of the handoff sets the palette, the spacing and the
 * shape of every control; option 1d ("Nocturne") supplies the dark palette.
 *
 * The accessibility floor of §15 is built in rather than checked afterwards:
 * every control is at least 44px tall, nothing is conveyed by colour alone (the
 * chosen reaction carries a ✓ and aria-pressed, not just a fill), the layout is
 * a single column that survives 200% text scaling without horizontal scrolling,
 * and the only motion is a fade that `prefers-reduced-motion` removes.
 */

/*
 * Golos Text is a variable font: one file per subset carries the whole weight
 * axis, so `font-weight: 400 600` here is one download rather than the three a
 * per-weight rule would fetch.
 *
 * `font-display: swap` is the right trade for this product. The alternative
 * blocks the first paint on a font file, and what is behind that paint is a
 * short text somebody wrote by hand — it should arrive in the system face
 * immediately and be reset a moment later, not wait.
 */
@font-face {
  font-family: "Golos Text";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/static/fonts/golos-text-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}

/* The subset every note is actually written in (§3: pilot.language = ru). */
@font-face {
  font-family: "Golos Text";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/static/fonts/golos-text-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/*
 * Latin only, and static instances rather than a variable file: the mono face
 * sets invite codes (Crockford base32 — uppercase Latin and digits) and the
 * character counter (digits and a slash). Neither can contain Cyrillic.
 */
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/ibm-plex-mono-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/ibm-plex-mono-500-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}

:root {
  color-scheme: light dark;

  --ground: #fbfaf8;
  --surface: #f4f1ec;
  --field: #ffffff;
  --ink: #1c1a18;
  /* 4.9:1 on --ground. Every muted line on the screen uses this one, so the
     contrast audit has one number to check rather than six. */
  --ink-muted: #5b5651;
  /* Hairlines between sections. Decorative, so no contrast floor applies. */
  --line: rgba(28, 26, 24, 0.14);
  /*
   * The border of every form control and of the neutral button, so WCAG 1.4.11
   * applies: non-text contrast of 3:1 against whatever it sits on. The design's
   * 0.34 measures 2.13:1 and fails; 0.52 is the lightest value that clears 3:1
   * on both the ground and the surface (3.4:1 and 3.3:1). It is a visible change
   * from the mockup, and the alternative is a field whose edge some people
   * cannot find.
   */
  --line-strong: rgba(28, 26, 24, 0.52);
  /* oklch(0.46 0.1 50) from the handoff: 7.4:1 on --ground, and the only accent
     in the design. --accent-quiet is decorative only and never carries meaning. */
  --accent: #8a4a24;
  --accent-ink: #fffaf4;
  --accent-quiet: #c2733f;
  --focus: #1c5fb8;

  --radius: 14px;
  --radius-lg: 18px;
  --tap: 44px;

  /* The fallback is a real stack, not a formality: `swap` renders in it first. */
  --font: "Golos Text", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --ground: #17171a;
    --surface: #212127;
    --field: #1d1d22;
    --ink: #f4efe8;
    --ink-muted: #b3aca3;
    --line: rgba(244, 239, 232, 0.18);
    /* 3.5:1 on the dark ground — already clear, and left where the design put it. */
    --line-strong: rgba(244, 239, 232, 0.4);
    --accent: #e8b57e;
    --accent-ink: #1b1608;
    --accent-quiet: #e8b57e;
    --focus: #8ec5ff;
  }
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  /* No font-size override: a participant who has enlarged their browser text
     has said what they want, and 200% of 16px must still lay out. */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font);
  /*
   * `1rem`, never `16px`. A px value here would override the default text size
   * somebody has set in their browser, which is how most people who need larger
   * text get it — and §15 requires the layout to survive 200% scaling, not to
   * quietly refuse it. Everything below sizes in rem for the same reason.
   */
  font-size: 1rem;
  line-height: 1.55;
  /* The reading column, and the reason the layout survives zoom: it is one
     column at every width, and 200% text simply makes it taller. */
  max-width: 34rem;
  margin-inline: auto;
  /*
   * The gutter grows with the viewport, and the clamp's floor is the phone
   * value: a participant's column is capped at 34rem and centred, so this is
   * only ever visible on the console, which is capped at 52rem and was left
   * with a 20px margin at every width between the two — the widths a laptop
   * half-screen actually lands on.
   */
  padding: 0 clamp(1.25rem, 5vw, 3rem);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* --- skip link and focus ------------------------------------------------ */

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
}

.skip:focus {
  position: static;
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  margin: 0.5rem 0;
  padding: 0.75rem 1rem;
  background: var(--accent);
  color: var(--accent-ink);
  border-radius: var(--radius);
}

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

/* --- structure ---------------------------------------------------------- */

.masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 1rem 0 0;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  gap: 0.6rem;
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
}

.wordmark::before {
  content: "";
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background: var(--accent-quiet);
  flex: none;
}

.wordmark--muted::before {
  background: var(--line-strong);
}

main {
  flex: 1;
  padding: 1.5rem 0 2rem;
}

.site-footer {
  border-top: 1px solid var(--line);
  padding: 1.25rem 0 2rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  font-size: 0.85rem;
  color: var(--ink-muted);
}

/* --- type --------------------------------------------------------------- */

h1 {
  margin: 0 0 0.75rem;
  font-size: 1.6rem;
  line-height: 1.3;
  font-weight: 500;
  letter-spacing: -0.02em;
}

h2 {
  margin: 0 0 0.6rem;
  font-size: 1.15rem;
  line-height: 1.35;
  font-weight: 600;
  letter-spacing: -0.01em;
}

/*
 * The region label. It is a real heading for a screen reader — §11's three
 * regions are how the screen is navigated — and quiet for everyone else,
 * because the participant is not here to admire an information architecture.
 */
.region-label {
  margin: 0 0 0.5rem;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--ink-muted);
  text-transform: none;
}

p {
  margin: 0 0 0.85rem;
  text-wrap: pretty;
}

.lead {
  font-size: 1.35rem;
  line-height: 1.35;
  font-weight: 500;
  letter-spacing: -0.02em;
}

.muted {
  color: var(--ink-muted);
  font-size: 0.9rem;
}

.date-line {
  color: var(--ink-muted);
  font-size: 0.85rem;
  margin-bottom: 0.75rem;
}

/* --- regions and cards --------------------------------------------------- */

/*
 * `+ *` rather than `+ .region`: what follows the last region on the home
 * screen is «На сегодня всё» (§11), which belongs to the screen and not to the
 * region above it. With only the paragraph rhythm between them it read as one
 * more line of «Твоя записка».
 */
.region + * {
  margin-top: 1.75rem;
}

.card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: 1.35rem 1.25rem;
}

.card--outline {
  background: transparent;
  border: 1px solid var(--line);
}

/* I-3: an honest absence looks like an absence, not like a missing card. */
.card--empty {
  background: transparent;
  border: 1px dashed var(--line-strong);
}

.card--quiet {
  background: var(--surface);
  border-left: 2px solid var(--accent-quiet);
  border-radius: 0 var(--radius) var(--radius) 0;
}

.card > :last-child,
.card > form:last-child > :last-child {
  margin-bottom: 0;
}

/*
 * A card has an edge, and what follows one is a different thing: the sentence
 * that qualifies it, the next card, the button under it. Cards carried no
 * margin at all, so on the home screen «Одна записка уже ждёт проверки» sat
 * flush against the card above it — closer to it than two unrelated sentences
 * are to each other, which reads as a line that fell out of the card.
 *
 * After rather than before, because a region label sits 8px above its card and
 * that closeness is right. It collapses through the region's bottom edge into
 * the larger `.region + .region`, so the distance between regions is unchanged.
 */
.card {
  margin-bottom: 1rem;
}

/* --- the note itself ----------------------------------------------------- */

.origin {
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  color: var(--ink-muted);
  margin: 0.5rem 0 1rem;
}

.note-text {
  font-size: 1.4rem;
  line-height: 1.55;
  letter-spacing: -0.015em;
  margin: 0 0 2rem;
  /* A note is up to 300 characters of someone else's writing; its own line
     breaks are part of it. */
  white-space: pre-wrap;
}

/*
 * The rule under the text is doing work, not decoration: above it is what
 * somebody wrote, below it is what this reader may do about it, and the two
 * should not read as one block.
 */
.note-actions {
  border-top: 1px solid var(--line);
  padding-top: 1.25rem;
}

/*
 * «Пожаловаться», kept away from the reactions. §7.3 asks for it to be
 * visually and semantically distinct from «Сегодня не моё», and adjacency is
 * most of what would make the two read as degrees of the same thing.
 */
.note-footer {
  margin-top: 2.5rem;
  border-top: 1px solid var(--line);
  display: flex;
  justify-content: flex-end;
}

/* --- controls ------------------------------------------------------------ */

.actions {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin: 1.5rem 0 0.85rem;
}

/*
 * The selector is `.button` alone and never `button.button`: a type selector
 * here would out-specify every modifier below, and the two outline reactions
 * would both render filled — which is exactly the "state conveyed by colour"
 * failure §15 forbids, arrived at by a stylesheet accident.
 */
.button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: 100%;
  min-height: 52px;
  padding: 0.75rem 1rem;
  border: none;
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--accent-ink);
  font: inherit;
  font-weight: 500;
  font-size: 1.05rem;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}

.button--secondary {
  background: transparent;
  color: var(--accent);
  border: 1.5px solid var(--accent);
}

.button--neutral {
  background: transparent;
  color: var(--ink);
  border: 1.5px solid var(--line-strong);
}

/*
 * The selected reaction. It is filled *and* prefixed with a ✓ and marked
 * aria-pressed: §15 forbids conveying an action by colour alone, and this is the
 * one control on the screen that holds a state.
 */
.button--chosen {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}

.button--plain {
  background: transparent;
  color: var(--ink-muted);
  border: none;
  font-size: 1rem;
  font-weight: 400;
  min-height: var(--tap);
}

/*
 * Every link in this product is a destination of its own — a back link, a
 * footer link, «Пожаловаться» — never a word inside a sentence. So they all get
 * the 44px target §15 requires, rather than only the ones somebody remembered.
 */
.link {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* The back link sits above a heading and needs the gap a bare link lacks. */
main > .link:first-child {
  margin-bottom: 0.5rem;
}

/* Any link that is a target for a finger, not a word inside a sentence. */
.link-row {
  display: flex;
  align-items: center;
  min-height: 48px;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
}

.link-row:last-child {
  border-bottom: none;
}

/* --- forms --------------------------------------------------------------- */

/*
 * The control lives *inside* its label (`labelledField`), so these two margins
 * are not the pair they look like: `margin-bottom` here falls after the field,
 * not between the label's text and it. Setting the small value on the label was
 * the bug — it left 0px under «Код из приглашения», 6px above «Дальше», and
 * nothing at all between the button and the line under it, so the whole form
 * read as one block instead of a field, an action, and a note about it.
 */
label {
  display: block;
  font-weight: 500;
  margin-bottom: 1.25rem;
}

/* The label's own text to its field: close, because they are one thing. */
label > input[type="text"],
label > input[type="password"],
label > textarea,
label > select {
  margin-top: 0.45rem;
}

input[type="text"],
input[type="password"],
textarea,
select {
  width: 100%;
  min-height: var(--tap);
  padding: 0.7rem 0.9rem;
  border: 1.5px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--field);
  color: var(--ink);
  font: inherit;
}

textarea {
  min-height: 9rem;
  line-height: 1.5;
  resize: vertical;
}

/*
 * The sentence that follows a form — the pre-submit notice of §6.2, the «что
 * дальше» line of §4.2 — is about the form, not part of it. `p`'s margin is
 * bottom-only, so without this it sits flush against the button it qualifies.
 */
form + p {
  margin-top: 1rem;
}

/*
 * The same, for a button that is a link rather than a form: «Исправить и
 * отправить снова» after a rejection, with the sentence about the day slot
 * under it. Buttons inside `.actions` or inside a card are spaced by their own
 * container, and neither is a `.button + p` pair, so this reaches only the ones
 * standing on their own.
 */
.button + p {
  margin-top: 1rem;
}

/*
 * `input.code-field`, not `.code-field`. The base rule above is
 * `input[type="text"]`, which carries a type selector and therefore outranks a
 * bare class — so a plain `.code-field` would lose its `font-family` to the
 * `font: inherit` it inherits, silently, and the invite code would set in the
 * body face. The same trap already cost the reaction buttons their outlines.
 */
input.code-field {
  font-family: var(--font-mono);
  font-size: 1.3rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  border-color: var(--accent-quiet);
}

/*
 * The report's category group. A real fieldset with a real legend, because the
 * question has to be announced once for the whole group rather than repeated
 * against every option — but with the browser's default border removed, which
 * belongs to a different decade.
 */
fieldset {
  margin: 0 0 1.25rem;
  padding: 0;
  border: none;
}

legend {
  padding: 0;
  margin-bottom: 0.6rem;
  font-weight: 500;
}

.checkbox + .checkbox {
  margin-top: 0.5rem;
}

.checkbox {
  display: flex;
  align-items: flex-start;
  gap: 0.85rem;
  min-height: 60px;
  padding: 1rem 1.1rem;
  background: var(--surface);
  border-radius: var(--radius);
  font-weight: 400;
  cursor: pointer;
}

.checkbox input {
  width: 1.5rem;
  height: 1.5rem;
  margin: 0.15rem 0 0;
  flex: none;
  accent-color: var(--accent);
}

.field-note {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.75rem;
  margin: 0.5rem 0 0;
  font-size: 0.85rem;
  color: var(--ink-muted);
}

/*
 * The counter appears from 240 characters (§6.2) and turns into an explicit
 * message past 300 — never a silent truncation. Without JavaScript it is simply
 * absent and the server still refuses an over-long text.
 */
.counter {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  flex: none;
}

.counter[hidden] {
  display: none;
}

.counter--over {
  color: var(--accent);
  font-weight: 500;
}

/* The four openers of §6.2. Chips, and nothing more: they insert text. */
.openers-label {
  margin-top: 1.5rem;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0.75rem 0 1rem;
  padding: 0;
  list-style: none;
}

.chip {
  min-height: var(--tap);
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--line-strong);
  border-radius: 20px;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
  text-align: left;
}

.steps {
  display: flex;
  gap: 0.4rem;
  margin: 0 0 1.25rem;
  padding: 0;
  list-style: none;
}

.steps li {
  width: 2.1rem;
  height: 3px;
  border-radius: 2px;
  background: var(--line);
}

.steps li[data-done] {
  background: var(--accent-quiet);
}

/* --- lists --------------------------------------------------------------- */

.rules {
  margin: 0 0 1.25rem;
  padding: 0;
  list-style: none;
}

.rules li {
  display: flex;
  gap: 0.75rem;
  padding: 0.85rem 0;
  border-top: 1px solid var(--line);
  color: var(--ink);
}

.rules li::before {
  content: "—";
  color: var(--accent-quiet);
  flex: none;
}

.rules li:last-child {
  border-bottom: 1px solid var(--line);
}

.facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.6rem 1rem;
  margin: 1.5rem 0 0;
  padding: 1.1rem 1.25rem;
  background: var(--surface);
  border-radius: var(--radius);
  font-size: 0.9rem;
}

.facts dt {
  color: var(--ink-muted);
}

.facts dd {
  margin: 0;
}

/* --- the banner ---------------------------------------------------------- */

/*
 * One banner, announced to a screen reader when it appears. It carries the
 * refusal a redirect brought back, so it is a sentence the participant can act
 * on rather than a code.
 */
.banner {
  border-left: 2px solid var(--accent-quiet);
  background: var(--surface);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 0.9rem 1.1rem;
  margin: 0 0 1.25rem;
  animation: fade-in 160ms ease-out;
}

.banner p {
  margin: 0;
}

/* --- the envelope -------------------------------------------------------- */

/*
 * I-1. A card and a folded flap, drawn in CSS — "a simple card or envelope
 * suffices; expensive animation is out of scope" (§15). It is decorative, so it
 * is hidden from the accessibility tree and the sentence beside it carries the
 * meaning.
 */
.envelope {
  width: 6.5rem;
  height: 4.25rem;
  margin: 0 auto 1.25rem;
  border: 1px solid var(--line-strong);
  border-radius: 5px;
  background: var(--field);
  position: relative;
  overflow: hidden;
}

/*
 * The flap: a square rotated 45° with two of its borders drawn, so the corner
 * that shows is the V of a folded envelope. A clip-path would cut the border
 * off with the shape and leave nothing to see.
 */
.envelope::after {
  content: "";
  position: absolute;
  top: -2.1rem;
  left: 50%;
  width: 4.1rem;
  height: 4.1rem;
  margin-left: -2.05rem;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  transform: rotate(45deg);
}

.sealed {
  text-align: center;
}

.sealed .lead {
  margin-bottom: 1.25rem;
}

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

@keyframes fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* --- the moderator's desk ------------------------------------------------ */

/*
 * The console is the least subtle code in the project and it is not a
 * participant screen: it gets a wider column and no styling beyond legibility.
 */
.desk {
  max-width: 52rem;
}

/*
 * The console is a column of separate operations, each one a form that ends in
 * its button, and whatever follows a button is the next thing to do: another
 * form, the heading of the next section, a line of the log. So the gap after a
 * form is the one that says an action ended, and it has to be bigger than the
 * 1.25rem a field keeps from the field below it — at that distance the button
 * read as one more control in a single endless form. A heading was worse than
 * the rest: `h2` and `h3` carry no top margin at all, so «Загрузка стартовых
 * записок» landed flush on the button of the sanction form above it.
 *
 * Approve and reject are the case this started as, and they are covered by the
 * same rule: two forms, not two buttons — a rejection carries a reason and a
 * sentence, and there is no "approve with corrections" path (§8.2). Adjacent,
 * they read as one control group, which is the wrong thing to make easy. The
 * rule is scoped to the desk because a participant's sibling forms are inside
 * `.actions` and have their own gap, and because `form + p` on their screens is
 * a sentence about the form it follows rather than a separate thing.
 */
.desk form + * {
  margin-top: 2rem;
}

/*
 * The console's tab bar. Links, because each tab is an address of its own — the
 * back button, a bookmark and a link to «жалобы» all have to work, and none of
 * them would if this were script and a hidden panel.
 *
 * The current tab is marked three ways for the one reason §15 gives: colour is
 * never the only carrier of state. `aria-current` says it to a screen reader,
 * the weight says it in a screenshot, and the darker border says it to an eye
 * that skims. Wrapping is deliberate — an organiser has seven of these, and at
 * 320px or 200% text they become two rows rather than a horizontal scroll.
 */
.desk .tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 0 1.75rem;
  padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--line);
}

.desk .tabs a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: 0 0.85rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  color: var(--ink-muted);
  text-decoration: none;
  font-size: 0.95rem;
}

/* Filled, not tinted: the one on this screen has to be findable in the second
   somebody comes back to the console, and the fill inverts with the theme
   because both `--ink` and `--ground` do. */
.desk .tabs a[aria-current="page"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--ground);
  font-weight: 600;
}

.desk .queue-item {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.25rem;
  margin-bottom: 1.25rem;
}

.desk .queue-meta {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--ink-muted);
}

/* Author display rules must not expose hidden reminder/install controls. */
[hidden] { display: none !important; }
