/* Panel shell. RTL first: logical properties everywhere, never margin-left. */

@font-face {
  font-family: "Pinar";
  src: url("../fonts/Pinar-VF.woff2") format("woff2-variations");
  font-weight: 300 900;
  font-display: swap;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

/* Jalali dates: Persian digits in the brand font, still column aligned. */
.date {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Numbers and identifiers always read left to right in a fixed-width column. */
.num,
input[type="number"],
input[dir="ltr"],
code,
.mono {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  direction: ltr;
  unicode-bidi: isolate;
}

a {
  color: var(--cyan);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* ── layout ── */

.layout {
  display: grid;
  grid-template-columns: 244px 1fr;
  min-height: 100dvh;
}

.sidebar {
  background: var(--surface);
  border-inline-start: 0;
  border-inline-end: 1px solid var(--line);
  padding: var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-weight: 700;
}

.sidebar-brand img {
  width: 34px;
  height: 34px;
}

/* The collapse button only exists on narrow screens. */
.nav-toggle {
  display: none;
  margin-inline-start: auto;
}

.nav-group-title {
  font-size: 12px;
  color: var(--ink-faint);
  margin-block: var(--sp-3) var(--sp-1);
}

.nav a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: 8px 10px;
  border-radius: var(--r-md);
  color: var(--ink-mid);
}

.nav a:hover {
  background: var(--surface-2);
  text-decoration: none;
}

.nav a.active {
  background: var(--gold-bg);
  color: var(--gold);
  font-weight: 700;
}

.nav .tag-soon {
  font-size: 11px;
  color: var(--ink-faint);
  background: var(--sunk);
  border-radius: var(--r-sm);
  padding: 1px 6px;
}

.main {
  padding: var(--sp-5);
  min-width: 0;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-block-end: var(--sp-5);
  flex-wrap: wrap;
}

.topbar h1 {
  margin: 0;
  font-size: 20px;
}

.topbar .subtitle {
  margin: 2px 0 0;
  color: var(--ink-soft);
  font-size: 13px;
}

.topbar-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.whoami {
  font-size: 13px;
  color: var(--ink-soft);
}

/* ── cards, tables, forms ── */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
}

.grid {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
}

.stat {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.stat .stat-title {
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.stat .stat-message {
  color: var(--ink-soft);
  font-size: 13px;
}

.dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: none;
}

.dot-ok {
  background: var(--ok);
}

.dot-warn {
  background: var(--warn);
}

.dot-fail {
  background: var(--danger);
}

.dot-skipped {
  background: var(--ink-faint);
}

.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
}

.badge-ok {
  background: var(--ok-bg);
  color: var(--ok);
}

.badge-warn {
  background: var(--warn-bg);
  color: var(--warn);
}

.badge-fail {
  background: var(--danger-bg);
  color: var(--danger);
}

.badge-skipped {
  background: var(--t1-bg);
  color: var(--t1);
}

.badge-neutral {
  background: var(--surface-2);
  color: var(--ink-mid);
}

/* Every wide block scrolls inside its own container - the page body never does. */
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

th,
td {
  text-align: start;
  padding: 10px 12px;
  border-block-end: 1px solid var(--line);
  vertical-align: middle;
}

th {
  background: var(--surface-2);
  font-size: 13px;
  color: var(--ink-mid);
  white-space: nowrap;
}

tr:last-child td {
  border-block-end: 0;
}

label {
  display: block;
  font-size: 13px;
  color: var(--ink-mid);
  margin-block-end: 4px;
}

input,
select,
textarea {
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--ink);
  font-family: inherit;
  font-size: 14px;
}

input:focus,
select:focus,
textarea:focus {
  outline: 2px solid var(--cyan);
  outline-offset: 1px;
}

.field {
  margin-block-end: var(--sp-3);
}

.field-row {
  display: flex;
  gap: var(--sp-2);
  align-items: flex-end;
  flex-wrap: wrap;
}

.hint {
  font-size: 12px;
  color: var(--ink-faint);
  margin-block-start: 4px;
}

/* ── buttons: exactly one gold button per screen ── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 16px;
  border-radius: var(--r-md);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}

.btn:hover {
  background: var(--surface-2);
  text-decoration: none;
}

/* 🔴 A disabled button that looks like an enabled one is a lie the page tells.
 *
 * There was no `:disabled` rule in this file at all. Measured on the running
 * panel: the locked «تأیید و انتشار» came back `background: rgb(168,117,22)`,
 * `opacity: 1`, `cursor: pointer` - the same gold, the same pointer, pixel for
 * pixel the same button as one that works. A person clicks it, nothing happens,
 * and they click it again.
 *
 * `!important` on the background because `.btn-primary` and `.btn-danger` set
 * their own and both are exactly as specific as this rule; without it the
 * declaration order decides, which is how the rule came to look present and do
 * nothing on the one button that needed it.
 */
.btn:disabled,
.btn[disabled] {
  background: var(--sunk) !important;
  border-color: var(--line) !important;
  color: var(--ink-faint) !important;
  cursor: not-allowed;
}

.btn:disabled:hover,
.btn[disabled]:hover {
  background: var(--sunk) !important;
}

.btn-primary {
  background: var(--gold);
  border-color: var(--gold);
  color: #fff;
}

.btn-primary:hover {
  background: var(--gold-lift);
  border-color: var(--gold-lift);
}

.btn-ghost {
  background: transparent;
  border-color: transparent;
  color: var(--ink-soft);
  font-weight: 400;
}

.btn-danger {
  color: var(--danger);
  border-color: var(--danger);
}

.btn-sm {
  padding: 5px 10px;
  font-size: 13px;
}

/* ── flash messages ── */

.flash {
  border-radius: var(--r-md);
  padding: 10px 14px;
  margin-block-end: var(--sp-3);
  font-size: 14px;
}

.flash-ok {
  background: var(--ok-bg);
  color: var(--ok);
}

.flash-error {
  background: var(--danger-bg);
  color: var(--danger);
}

.flash-warn {
  background: var(--warn-bg);
  color: var(--warn);
}

/* ── login ── */

.login-shell {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: var(--sp-4);
}

.login-card {
  width: min(380px, 100%);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  padding: var(--sp-5);
}

/* The enrolment QR. A block of its own, because `.login-card img` below sizes
   the logo to 56px and an `<img>` QR would have come out unscannable with no
   error anywhere. Inline `<svg>`, so `style-src` never enters into it. */
.qr-code {
  display: flex;
  justify-content: center;
  margin-block-end: var(--sp-4);
}

.qr-code svg {
  border-radius: var(--r-md);
  background: #ffffff;
  padding: var(--sp-2);
}

.login-card img {
  width: 56px;
  height: 56px;
  margin-block-end: var(--sp-3);
}

/* ── empty / soon ── */

.placeholder {
  text-align: center;
  color: var(--ink-soft);
  padding: var(--sp-7) var(--sp-4);
}

.filters {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
  align-items: flex-end;
  margin-block-end: var(--sp-3);
}

.filters .field {
  margin-block-end: 0;
  min-width: 150px;
}

.pager {
  display: flex;
  gap: var(--sp-2);
  align-items: center;
  justify-content: center;
  margin-block-start: var(--sp-3);
}

details.detail-cell summary {
  cursor: pointer;
  color: var(--cyan);
  font-size: 13px;
}

details.detail-cell pre {
  margin: 6px 0 0;
  padding: 8px;
  background: var(--sunk);
  border-radius: var(--r-sm);
  font-size: 12px;
  max-width: 420px;
  overflow-x: auto;
}

/* ── responsive ── */

@media (max-width: 860px) {
  .layout {
    grid-template-columns: 1fr;
  }

  .sidebar {
    border-inline-end: 0;
    border-block-end: 1px solid var(--line);
  }

  .nav-toggle {
    display: inline-flex;
  }

  .nav {
    display: none;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 4px;
  }

  .nav[data-open="true"] {
    display: grid;
  }

  .nav-group-title {
    grid-column: 1 / -1;
  }

  .main {
    padding: var(--sp-4);
  }
}

/* ── a shell command, in a block of its own ──────────────────────────────────
   Left to right, monospace, and never inside a sentence: the bidirectional
   algorithm rearranges a latin command that sits in Persian prose, and the
   backup page printed `make import FILE=…` as `...=make import FILE` to
   somebody in the middle of a restore. The copy button is the other half - the
   operator should not be retyping this at all. */
.command {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-3);
}

.command pre {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-2);
  direction: ltr;
  text-align: left;
  overflow-x: auto;
  white-space: pre;
}

.command code {
  font-size: 13px;
  color: var(--ink);
}

.command .btn {
  flex: none;
}

/* Two buttons on one form, the primary one first. */
.field-actions {
  display: flex;
  align-items: flex-end;
  gap: var(--sp-2);
}

/* ── a number a person reads ─────────────────────────────────────────────────
   Not `.mono`. `.mono` forces `direction: ltr`, which is right for an
   identifier and wrong for an amount: inside an LTR cell a band written
   «low – high» is displayed exactly as written, so a Persian reader coming from
   the right meets the **larger** number first. Left alone, the bidirectional
   algorithm puts the first number on the right, where reading starts.

   Tabular figures so columns of money line up and can be compared by length,
   which is the only reason the column is narrow enough to compare at all. */
.amount {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The identifier under a name: present for the operator who needs to paste it,
   quiet enough that the name is what the eye lands on. */
.person-id {
  display: block;
  font-size: 12px;
  color: var(--ink-soft);
}

/* Day · month · year, in that order, reading right to left. */
.date-parts {
  display: flex;
  gap: var(--sp-2);
}

.date-parts select {
  width: auto;
  min-width: 5.5rem;
}

.check-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  align-items: center;
}

.field-label {
  display: block;
  font-size: 13px;
  color: var(--ink-mid);
  margin-block-end: 4px;
}

/* An empty table is three sentences and sometimes a button, not one line. */
.empty-title {
  margin: 0 0 4px;
  font-weight: 600;
  color: var(--ink);
}

.empty-why {
  margin: 0 0 var(--sp-3);
  color: var(--ink-soft);
}

/* ── controls inside a table row ─────────────────────────────────────────────
   These were sized in pixels - `width: 80px` on a select, `width: 90px` on
   another - and Persian text plus a dropdown arrow does not fit in ninety
   pixels. The events table showed «بله» as `بلا`, «فعال» as a single `ذ`, and
   a four digit year as `20`. Sized in `ch` instead, which is the unit that
   knows how wide a character is, with room left for the arrow. */
.cell-select {
  /* `auto` is the whole point: a select sizes itself to its widest option, so
     nothing is clipped and nothing is padded out either. A `min-width` here was
     the second version of the same mistake - it stopped the clipping and pushed
     the last five columns of the events table off the screen instead. */
  width: auto;
  max-width: 14rem;
  padding-inline-end: 26px;
}

.cell-num {
  width: 7ch;
  text-align: center;
}

.cell-money {
  width: 11ch;
}

/* A control that edits one field of one row belongs under that field's
   heading. `width: 100%` on a select inside a cell stretched it across
   whatever the column happened to be, so the role editor on the admins page
   started under «آخرین ورود» and ended under «نقش». */
td .field-row {
  justify-content: flex-start;
}

td select,
td input:not([type="checkbox"]) {
  width: auto;
}

/* Several controls in one cell: they wrap inside the cell rather than pushing
   it wider than its heading. The review queue's reject box used the global
   `width: 100%` and ended up sitting to the left of the «عملیات» column it
   belongs to. */
.row-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}

.cell-reason {
  width: 16rem;
  max-width: 100%;
}

.stat-fix {
  display: inline-block;
  margin-block-start: var(--sp-2);
  font-size: 13px;
}

.cell-chat-id {
  width: 16ch;
}

/* The machine key under the sentence it explains: its own line, quieter. */
td .hint {
  display: block;
  margin: 2px 0 0;
  font-size: 12px;
}

/* A search box is a search box, not a full-width banner: at 100% it pushed its
   own button onto the next line. */
.filters .input,
.filters input[type="search"] {
  width: auto;
  flex: 1 1 20rem;
  max-width: 26rem;
}

/* …and its neighbours, or a full-width select drops the search box onto a line
   of its own and the row stops reading as one control. */
.filters select {
  width: auto;
  min-width: 9rem;
  max-width: 16rem;
}

/* The chat id and its save button belong on one line. */
.cell-chat-id + .btn,
td form.row-actions {
  flex-wrap: nowrap;
}

/* ── the claimed global rankings, on the review screen ─────────────────────
 *
 * Ali gave no separate proof for a top global ranking - «روی ویدئو مشخص هست و
 * خود کاربر ویدئو رو می‌گیره» - so the expert compares a claim against the
 * video the seller already sent. That only works if the claim is beside the
 * video buttons rather than three cards further down inside a JSON blob, and
 * if it is loud enough to be read before the approve button is pressed. */
.card-flag {
  border-color: var(--warn);
}

.plain-list {
  list-style: none;
  margin: 0 0 var(--sp-3);
  padding: 0;
  display: grid;
  gap: 4px;
}

.plain-list li {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 5px 0;
  border-block-end: 1px solid var(--line);
}

.note-danger {
  background: var(--danger-bg);
  color: var(--danger);
  font-weight: 600;
}

/* The publication lock's own line. Amber rather than red: nothing has gone
 * wrong, there is work left - and the design handoff's fallback for this row
 * («اگر تأیید نشود چه می‌شود») is exactly «هشدار کهربایی». */
.note-warn {
  background: var(--warn-bg);
  color: var(--warn);
  font-weight: 600;
}

/* ── module 7: «متن‌ها» ─────────────────────────────────────────────────────
 *
 * These live here and not in a `style=` attribute, and that is not a
 * preference. The panel's CSP is `style-src 'self'`, which blocks inline style
 * attributes as well as inline `<style>` blocks - so the first version of this
 * page had a dozen inline rules that the browser silently dropped. Measured,
 * not guessed: the default block came back `font-family: monospace;
 * white-space: pre` (the user-agent default) and the edit box was 199px wide
 * inside a 954px row, because `flex: 1 0 100%` never applied either.
 */

.msg-group > summary {
  cursor: pointer;
  font-weight: 700;
  display: flex;
  gap: var(--sp-2);
  align-items: baseline;
}

.msg-row {
  border-block-start: 1px solid var(--line);
  padding-block: var(--sp-2);
}

.msg-row > summary {
  cursor: pointer;
  display: flex;
  gap: var(--sp-2);
  align-items: baseline;
  flex-wrap: wrap;
}

/* A key is latin and reads left to right; isolating it stops the dots from
   being reordered inside a right-to-left line. */
.msg-key {
  direction: ltr;
  unicode-bidi: isolate;
  font-size: 12px;
}

.msg-preview {
  flex: 1 1 240px;
  font-size: 12px;
  color: var(--ink-faint);
}

/* The shipped sentence, always visible - «برگرداندن به پیش‌فرض» has to mean
   something an operator can read before they press it. `font-family: inherit`
   because a monospace Persian face draws every letter in a fixed cell and the
   words come out with gaps wide enough to look broken. */
.msg-default {
  white-space: pre-wrap;
  margin: 0 0 var(--sp-2);
  padding: var(--sp-2);
  background: var(--surface-2);
  border-radius: var(--r-md);
  font-family: inherit;
  font-size: 13px;
  line-height: 1.7;
}

/* `1 0 100%`, not `1 1 100%`: with shrink allowed, the save button pulled the
   box down to about two hundred pixels and sat beside it. The button belongs
   on the next line. */
.msg-field {
  flex: 1 0 100%;
}

/* ── §28.10: the fifty-six inline styles that were never applied ─────────────
 *
 * The panel's Content-Security-Policy is `style-src 'self'`, and CSP2 folds the
 * `style` **attribute** into that directive - only CSP3's `style-src-attr`
 * separates them. So every `style="…"` in a Jinja template here was dropped by
 * the browser, silently, with no error anywhere. Measured, not deduced: a fresh
 * `style="margin-block-end: 40px; font-size: 33px"` appended to the live page
 * comes back from `getComputedStyle` as `15px` / `15px`.
 *
 * That means these are not a refactor. Every rule below is a piece of layout
 * somebody wrote and nobody has ever seen - which is why parts of the panel
 * "looked badly designed" on the server.
 *
 * Loosening the policy to `'unsafe-inline'` would have been one line and the
 * wrong line: it re-opens the hole for every injected attribute too. Classes
 * cost nothing and the policy stays.
 *
 * They are named for what they are rather than for what they measure, except
 * the four spacing helpers - a section heading is a section heading on six
 * pages, and giving each its own class would be six names for one idea. */

.mt-2 {
  margin-block-start: var(--sp-2);
}

.mt-3 {
  margin-block-start: var(--sp-3);
}

.mt-4 {
  margin-block-start: var(--sp-4);
}

.mb-3 {
  margin-block-end: var(--sp-3);
}

.mb-4 {
  margin-block-end: var(--sp-4);
}

.ms-2 {
  margin-inline-start: var(--sp-2);
}

/* A heading inside a card: the review screens, the settings groups, the
   discount table. 16px, and the space under it is the card's own rhythm. */
.section-title {
  margin: 0 0 var(--sp-3);
  font-size: 16px;
}

/* The same, where it follows a block rather than opening one. */
.section-title-tight {
  margin: 0 0 var(--sp-2);
  font-size: 16px;
}

/* And the same again with air above it, for a second section on one page. */
.section-title-spaced {
  margin: var(--sp-5) 0 var(--sp-3);
  font-size: 16px;
}

/* The four pages a person meets before they are signed in: login, the second
   factor, the backup codes and the password change. Bigger, and tight to the
   sentence under it. */
.auth-title {
  margin: 0 0 4px;
  font-size: 19px;
}

.error-title {
  margin: 0 0 var(--sp-2);
  font-size: 18px;
}

.centered {
  text-align: center;
}

.wide {
  width: 100%;
}

/* An operator's own words, kept as they typed them. Without this a seller's
   description arrived in the review screen as one paragraph with its line
   breaks gone. */
.prewrap {
  white-space: pre-wrap;
}

/* The one number on a review card that the eye should land on first. */
.figure-lg {
  font-size: 17px;
  font-weight: 800;
}

.lead-strong {
  margin: 0;
  font-weight: 700;
}

/* Filter fields inside a `.filters` row. They grow, and the basis is what
   keeps a three-field row from collapsing into three narrow boxes. */
.grow-160 {
  flex: 0 1 160px;
}

.grow-200 {
  flex: 1 1 200px;
}

.grow-320 {
  flex: 1 1 320px;
}

/* The backup codes, which are a list of strings and not a bulleted argument. */
.plain-list {
  list-style: none;
  padding: 0;
  line-height: 2;
}

/* The dashboard's health banner: a traffic light and a counter on one side, the
   refresh on the other. It was written as an inline style spanning two lines
   and was therefore never once laid out this way on the server - the badge and
   the button stacked. */
.health-banner {
  margin-block-end: var(--sp-4);
  display: flex;
  gap: var(--sp-3);
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
}

/* ── and the thirty-nine §28.10 did not count ────────────────────────────────
 *
 * The audit walked `templates/*.html` and stopped there. The catalogue pages
 * and the pricing bench live in `templates/catalog/` and `templates/valuation/`,
 * and they carried thirty-nine more of exactly the same thing - so the real
 * number was ninety-six, not fifty-seven. The test below the sweep uses
 * `rglob`, which is how they were found. */

.mb-2 {
  margin-block-end: var(--sp-2);
}

.gap-1 {
  gap: var(--sp-1);
}

/* A heading inside a catalogue panel: one size down from `.section-title`,
   because these sit inside a tab that already has a title. */
.panel-title {
  margin: 0 0 var(--sp-2);
  font-size: 15px;
}

.text-15 {
  font-size: 15px;
}

/* The one figure on the pricing bench that is the answer. */
.figure-gold {
  font-size: 22px;
  color: var(--gold);
}

/* A list inside a card: indented, and without the paragraph margin that would
   push it away from the sentence introducing it. */
.inset-list {
  margin: 0;
  padding-inline-start: var(--sp-4);
}

.inset-list-spaced {
  margin: var(--sp-2) 0 0;
  padding-inline-start: var(--sp-4);
}

/* Numeric columns in the catalogue tables. Fixed widths so a column of counts
   does not resize itself as the page filters. */
.w-90 {
  width: 90px;
}

.w-110 {
  width: 110px;
}

.w-130 {
  width: 130px;
}

.w-140 {
  width: 140px;
}

.grow-180 {
  flex: 0 1 180px;
}

.grow-220 {
  flex: 1 1 220px;
}

/* ── the censor studio ───────────────────────────────────────────────────────
 *
 * The one page with a canvas on it. Everything here is layout: the drawing is
 * `censor.js` and the colours come from the same tokens as the rest of the
 * panel, so the studio follows the theme toggle like every other page.
 */
.censor-identity {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding-block-end: var(--sp-3);
  margin-block-end: var(--sp-3);
  border-block-end: 1px solid var(--line);
}

.censor-identity-spacer {
  flex: 1 1 auto;
}

.censor-toolbar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-block-end: var(--sp-3);
}

.censor-status {
  flex: 1 1 auto;
  font-size: 13px;
  color: var(--ink-soft);
}

/* A checkerboard, so a mask over a light screenshot is still visibly a mask and
 * not a hole in the page. Two gradients rather than an image: `img-src` allows
 * `data:` but a 20-line gradient is smaller than the base64 of a tile. */
.censor-canvas-wrap {
  display: flex;
  justify-content: center;
  padding: var(--sp-4);
  border-radius: var(--r-md);
  background-color: var(--sunk);
  background-image:
    linear-gradient(45deg, var(--line) 25%, transparent 25%),
    linear-gradient(-45deg, var(--line) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--line) 75%),
    linear-gradient(-45deg, transparent 75%, var(--line) 75%);
  background-size: 16px 16px;
  background-position: 0 0, 0 8px, 8px -8px, -8px 0;
  /* The canvas fits the width and keeps the picture's own height, so a phone
   * screenshot is 880x1900 and this is what stops the page from becoming two
   * metres long. `align-items: flex-start` because a `flex` container stretches
   * its child by default, which would distort the canvas rather than scroll it. */
  align-items: flex-start;
  max-height: 72vh;
  overflow: auto;
}

.censor-canvas-wrap canvas {
  /* `block`, or the inline baseline leaves a few pixels of gap under it that
   * look like a rendering fault. `touch-action: none` so a drag on a tablet
   * draws a mask instead of scrolling the page out from under it. */
  display: block;
  touch-action: none;
  cursor: crosshair;
  max-width: 100%;
  border-radius: var(--r-sm);
  box-shadow: 0 2px 12px rgb(0 0 0 / 25%);
}

/* ── the review queue's three columns ────────────────────────────────────────
 *
 * «هیچ رفت‌وبرگشتی» - the queue on the right, the account in the middle, the
 * decision on the left where it stays while the middle scrolls.
 *
 * 🔴 `minmax(0, 1fr)` on the middle track, not `1fr`. A grid track's default
 * minimum is `min-content`, and the seller's description is pasted text with
 * long unbroken runs in it - so `1fr` lets the middle column grow to whatever
 * the longest word needs and pushes the decision column off the screen. This is
 * the same class of defect as the flex item that would not scroll.
 */
.review-columns {
  display: grid;
  grid-template-columns: 250px minmax(0, 1fr) 320px;
  gap: var(--sp-4);
  align-items: start;
}

.review-queue-column,
.review-decide-column {
  position: sticky;
  top: var(--sp-4);
  align-self: start;
  /* Its own scroll, so a queue of fifty does not make the page fifty long and
   * the decision column does not scroll away from the account it is about. */
  max-height: calc(100vh - var(--sp-7));
  overflow: auto;
}

.review-queue-column {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-3);
}

.review-queue-list {
  list-style: none;
  margin: var(--sp-2) 0 0;
  padding: 0;
}

.review-queue-list li {
  border-block-start: 1px solid var(--line);
}

.review-queue-list a {
  display: grid;
  gap: 2px;
  padding: var(--sp-2);
  border-radius: var(--r-sm);
  text-decoration: none;
  color: var(--ink);
  font-size: 12.5px;
}

.review-queue-list a:hover {
  background: var(--surface-2);
}

/* The one being read, marked with the gold bar the handoff asks for. */
.review-queue-list li.is-current a {
  background: var(--gold-bg);
  border-inline-end: 3px solid var(--gold);
  font-weight: 700;
}

/* Over a day. A seller waiting since yesterday is a different thing from one
 * who submitted an hour ago, and a column of identical grey timestamps says
 * neither. */
.waited-long {
  color: var(--danger);
  font-weight: 700;
}

.review-prices {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-2);
  margin-block-end: var(--sp-3);
  padding-block-end: var(--sp-3);
  border-block-end: 1px solid var(--line);
}

.review-prices div {
  display: grid;
  gap: 2px;
}

/* Under a laptop's width there is no room for three columns, so they stack and
 * the two sides stop being sticky - a sticky element in a stacked layout pins
 * itself over the content underneath it. */
@media (max-width: 1200px) {
  .review-columns {
    grid-template-columns: 1fr;
  }

  .review-queue-column,
  .review-decide-column {
    position: static;
    max-height: none;
  }
}

/* ── the components the templates ask for and this file never answered ───────
 *
 * Phase 14 step 4.6, and found by a script rather than by reading: every class
 * a panel template names, compared with every class this stylesheet defines.
 * Nine came back missing, and they are not decorations -
 *
 *   .note            four pages use it for their most prominent explanatory
 *                    block, and `.note-warn` / `.note-danger` are **modifiers
 *                    of a base that did not exist** - so they carried a colour
 *                    and no shape, and rendered as loose coloured text
 *   .btn-block       three of the four pages a person sees before they are
 *                    logged in
 *   .pager-position  every paginated table
 *
 * `tests/test_panel_components.py` is the script, kept as a test, so the next
 * class somebody invents in a template cannot go unanswered in silence. That
 * silence is the whole problem: a missing rule looks exactly like a rule that
 * did nothing.
 */
.note {
  padding: var(--sp-3);
  border-radius: var(--r-md);
  background: var(--surface-2);
  color: var(--ink-mid);
  font-size: 13px;
  line-height: 1.8;
}

/* The two modifiers keep their colours; the base gives them a shape. */
.note-warn,
.note-danger {
  border: 1px solid currentcolor;
}

.btn-block {
  display: flex;
  width: 100%;
}

.pager-position {
  font-size: 12.5px;
  color: var(--ink-soft);
}

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

.error-text {
  color: var(--danger);
  font-size: 12.5px;
}

/* A checkbox and its label on one line, so the box is not on a line of its own
 * with the words beneath it - which is what an unstyled `<label>` wrapping an
 * input does at this font size. */
.checkbox {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: 13px;
  cursor: pointer;
}

.checkbox input {
  accent-color: var(--gold);
}

/* The restore card carries commands somebody types by hand under pressure. */
.restore {
  margin-block-start: var(--sp-4);
}

/* The events table is the longest in the panel and its rows are one line. */
.table-dense td,
.table-dense th {
  padding-block: 6px;
}
