/*
 * اجارستان — stylesheet
 *
 * Written mobile-first and RTL-native: logical properties (inline-start,
 * margin-inline) rather than left/right, so the layout is correct in Persian
 * without a second "RTL override" sheet fighting the first one. The legacy
 * site carried 121KB of CSS across a dozen files doing exactly that.
 *
 * Self-hosted throughout — no CDN, no external font.
 */

/* ------------------------------------------------------------------ fonts */

@font-face {
  font-family: 'Vazirmatn';
  src: url('/assets/fonts/vazirmatn-regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  /* swap: show the fallback immediately rather than leaving the page blank
     while the face loads on a slow connection. */
  font-display: swap;
}

@font-face {
  font-family: 'Vazirmatn';
  src: url('/assets/fonts/vazirmatn-medium.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Vazirmatn';
  src: url('/assets/fonts/vazirmatn-bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ------------------------------------------------------------------ tokens */

:root {
  /*
   * Taken from the wordmark: #ff7700, the orange the logo is drawn in.
   *
   * The site was blue, which put the only orange on the page inside the logo
   * and made it read as a sticker someone had dropped on a template. A brand
   * colour is not decoration — it is the thing a reader recognises before they
   * have read a word, and it has to be the same colour in the mark and on the
   * button beside it.
   *
   * --brand-ink is the darker step for text and borders: #ff7700 on white is
   * about 2.9:1, under the 4.5:1 that body-sized text needs, so anything
   * written in the brand colour uses this instead and the bright one is kept
   * for fills, where white text sits on top of it.
   */
  --brand: #ff7700;
  --brand-dark: #d95f00;
  --brand-ink: #a34500;
  --brand-light: #fff2e6;

  /*
   * What goes on top of a brand fill.
   *
   * White on #ff7700 is 2.7:1 — a button nobody with ordinary eyesight reads
   * comfortably and nothing close to the 4.5:1 a label needs. Near-black on the
   * same orange is 6.2:1, so the fill keeps the logo's exact colour and the
   * text changes instead. It is also the more confident look: a bright orange
   * button with dark type reads as deliberate, a washed-out white one as a
   * default nobody checked.
   */
  --on-brand: #16202c;

  --success: #0f7b3f;
  --warning: #b45309;
  --danger: #c0342b;

  --ink: #1a1d23;
  --ink-muted: #5b6472;
  /*
   * The quietest text on the page, and still text.
   *
   * It was #8b94a3 — 3.06:1 on white, under the 4.5:1 a small label needs and
   * close enough to the background that the ad counts beside each category
   * read as decoration rather than as numbers. Quiet is a matter of being the
   * third thing the eye reaches, not of being hard to read.
   */
  --ink-faint: #6b7480;

  --surface: #ffffff;
  --surface-alt: #f6f8fb;
  --surface-sunken: #eef2f7;

  --line: #dfe4ec;
  --line-strong: #c3cad6;

  --radius: 10px;
  --radius-sm: 6px;
  --radius-lg: 16px;

  --shadow-sm: 0 1px 2px rgb(16 24 40 / 6%);
  --shadow: 0 4px 12px rgb(16 24 40 / 8%);
  --shadow-lg: 0 12px 32px rgb(16 24 40 / 12%);

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;

  --container: 1240px;
  --header-height: 60px;

  --font: 'Vazirmatn', 'Segoe UI', Tahoma, system-ui, sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --ink: #e8eaed;
    --ink-muted: #9aa3b2;
    --ink-faint: #7b8494;
    --surface: #14171c;
    --surface-alt: #1b1f26;
    --surface-sunken: #23282f;
    --line: #2c323b;
    --line-strong: #3c434e;
    /* On a dark ground the bright orange is already legible, so the
       darker text step is not needed — it is lightened instead. */
    --brand-light: #3a2410;
    --brand-ink: #ff8c2b;
    --on-brand: #1a1205;
  }
}

/* ------------------------------------------------------------------- base */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.7;
  color: var(--ink);
  background: var(--surface-alt);
  /* Persian numerals render with the wrong metrics in some faces without this. */
  font-variant-numeric: lining-nums;
}

h1, h2, h3, h4, h5, h6 {
  margin: 0 0 var(--space-3);
  line-height: 1.45;
  font-weight: 700;
}

h1 { font-size: 1.5rem; }
h2 { font-size: 1.25rem; }
h3 { font-size: 1.05rem; }

p { margin: 0 0 var(--space-3); }

a {
  color: var(--brand-ink);
  text-decoration: none;
}

a:hover { text-decoration: underline; }

img, svg, video { max-width: 100%; height: auto; display: block; }

button, input, select, textarea { font: inherit; color: inherit; }

/* Focus is visible for keyboard users and invisible for mouse users. The
   legacy CSS removed outlines globally, which makes the site unusable
   without a mouse. */
:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  inset-inline-start: -9999px;
  top: 0;
  z-index: 100;
  padding: var(--space-3) var(--space-4);
  background: var(--brand);
  color: var(--on-brand);
}

.skip-link:focus { inset-inline-start: 0; }

.muted { color: var(--ink-muted); }
.small { font-size: 0.85rem; }

/* ----------------------------------------------------------------- header */

.header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  box-shadow: var(--shadow-sm);
}

.header__bar {
  max-width: var(--container);
  margin-inline: auto;
  min-height: var(--header-height);
  padding-inline: var(--space-4);
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  align-items: center;
  gap: var(--space-3);
}

.header__brand { flex-shrink: 0; }

.header__city {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-3);
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  white-space: nowrap;
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.header__search {
  position: relative;
  display: flex;
  align-items: center;
}

.header__search-input {
  width: 100%;
  padding: var(--space-2) var(--space-4);
  padding-inline-end: 40px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-sunken);
}

.header__search-btn {
  position: absolute;
  inset-inline-end: 4px;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  /*
   * Thirty-six square, not the eighteen-pixel icon inside it.
   *
   * A magnifier drawn at 18px with four pixels of padding is a 26px target,
   * and a fingertip is about nine millimetres — call it forty pixels. The icon
   * stays the size it looks right at; the button around it grows to something
   * a thumb can actually land on.
   */
  width: 36px;
  height: 36px;
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--ink-muted);
  padding: 0;
}

.header__search-btn:hover { color: var(--brand-ink); background: var(--surface-sunken); }

.header__suggest {
  position: absolute;
  top: calc(100% + 4px);
  inset-inline: 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  max-height: 320px;
  overflow-y: auto;
  z-index: 50;
}

.header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.header__account { position: relative; }

.header__account-btn {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  border-radius: 50%;
}

@media (max-width: 767px) {
  /*
   * Brand and account on the first row, city and search sharing the second.
   *
   * The previous arrangement put the city button between the brand and the
   * account buttons, where it got whatever was left over — on a 375px screen
   * that was 26 pixels, and the label «انتخاب شهر» was clipped to two
   * meaningless letters. Choosing a city is the first thing a visitor does
   * here, so it cannot be the control that gets crushed.
   */
  .header__bar {
    grid-template-columns: auto 1fr;
    grid-template-areas:
      'brand account'
      'city  search';
    row-gap: var(--space-2);
    padding-block: var(--space-2);
  }

  .header__brand { grid-area: brand; }
  .header__city { grid-area: city; max-width: none; }
  .header__search { grid-area: search; }

  .header__actions {
    grid-area: account;
    justify-self: end;
  }

  /* The + icon carries the meaning on a narrow screen; the words do not fit
     beside a login button and a logo. */
  .header__new-ad span { display: none; }
  .header__new-ad { padding-inline: var(--space-3); }
}

@media (max-width: 380px) {
  /* Scale the wordmark down rather than cropping it: object-fit: none on an
     SVG slices the logo in half instead of shrinking it. */
  .header__brand img { width: 92px; height: auto; }
}

/* ---------------------------------------------------------------- avatars */

.avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-sunken);
}

.avatar--placeholder {
  display: grid;
  place-items: center;
  font-weight: 700;
  color: var(--brand-ink);
  background: var(--brand-light);
}

/* -------------------------------------------------------------- dropdowns */

.dropdown {
  position: absolute;
  top: calc(100% + 8px);
  inset-inline-end: 0;
  min-width: 200px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: var(--space-2);
  z-index: 50;
}

.dropdown__header {
  padding: var(--space-2) var(--space-3);
  font-weight: 700;
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--space-2);
}

.dropdown a,
.dropdown button {
  display: block;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--ink);
  text-align: start;
  background: none;
  border: 0;
  cursor: pointer;
}

.dropdown a:hover,
.dropdown button:hover {
  background: var(--surface-sunken);
  text-decoration: none;
}

.dropdown__danger { color: var(--danger); }

.dropdown hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: var(--space-2) 0;
}

/* --------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-weight: 500;
  cursor: pointer;
  background: var(--surface-sunken);
  color: var(--ink);
  /* 44px is the minimum comfortable touch target. */
  min-height: 40px;
  transition: background-color 0.15s, border-color 0.15s;
}

.btn:hover { text-decoration: none; }

.btn--primary {
  background: var(--brand);
  color: var(--on-brand);
}

.btn--primary:hover { background: var(--brand-dark); color: #fff; }

.btn--ghost {
  background: transparent;
  border-color: var(--line-strong);
}

.btn--ghost:hover { background: var(--surface-sunken); }

.btn--danger { color: var(--danger); border-color: currentColor; }

.btn--block { width: 100%; }

/*
 * The apply/clear pair at the foot of the filters.
 *
 * Two full-width buttons in a row with no gap read as one control with a seam
 * down it, and the seam is exactly where a thumb lands. They are also not equal
 * in weight: applying is what the panel is for, clearing is the way out, so
 * clearing gets the quieter treatment and a little distance.
 */
.btn:disabled,
.btn[aria-busy='true'] {
  opacity: 0.6;
  cursor: not-allowed;
}

/* --------------------------------------------------------------- controls */

.input {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  min-height: 40px;
}

.input:focus {
  border-color: var(--brand);
  outline: none;
  box-shadow: 0 0 0 3px var(--brand-light);
}

.input--error { border-color: var(--danger); }

/*
 * `hidden` is a browser-default rule, which any class that sets `display`
 * silently beats. Everything here toggles visibility with `el.hidden`, so the
 * attribute has to be the one that wins.
 */
[hidden] { display: none !important; }

textarea.input { min-height: 120px; resize: vertical; }

.field { margin-bottom: var(--space-4); }

.field__label {
  display: block;
  margin-bottom: var(--space-1);
  font-weight: 500;
}

.field__hint {
  margin-top: var(--space-1);
  font-size: 0.85rem;
  color: var(--ink-muted);
}

.field__error {
  margin-top: var(--space-1);
  font-size: 0.85rem;
  color: var(--danger);
}

.check {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) 0;
  cursor: pointer;
}

/* ------------------------------------------------------------------ layout */

.site__main {
  max-width: var(--container);
  margin-inline: auto;
  padding: var(--space-5) var(--space-4);
  min-height: 60vh;
}

.section-title {
  font-size: 1.1rem;
  margin-bottom: var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--line);
}

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}

/* ------------------------------------------------------------------- ads */

/*
 * Two across on a phone, three on a desktop — a fixed count, not auto-fill.
 *
 * auto-fill on a wide screen produced six or seven cards to a row, each one a
 * thumbnail too small to tell two similar things apart, with the title cut off
 * and the photo cropped past whatever the seller wanted you to see. A listing
 * is a page people read, not a contact sheet: fewer, larger cards is the whole
 * difference between scanning and squinting.
 */
.ad-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
}

@media (min-width: 900px) {
  .ad-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-4);
  }
}

/*
 * The "similar ads" and "more from this seller" rails.
 *
 * Smaller than a listing card but not a contact sheet: auto-fill with a 150px
 * floor put seven across a wide screen, each one a thumbnail with its title cut
 * at three words — the same thing that was wrong with the main grid. A fixed
 * count keeps them readable, and the same two-across on a phone as everywhere
 * else.
 */
.ad-grid--compact {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 720px) {
  .ad-grid--compact { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 1100px) {
  .ad-grid--compact { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* These rails keep the card shape on a phone rather than becoming rows: they
   are a sideline to the page, and a row of full-width rows reads as the main
   listing starting again. */
@media (max-width: 719px) {
  .ad-grid--compact {
    gap: var(--space-3);
    border-top: 0;
  }

  .ad-grid--compact .ad-card {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
  }

  .ad-grid--compact .ad-card__link {
    display: block;
    padding: 0;
  }

  .ad-grid--compact .ad-card__media { aspect-ratio: 3 / 2; border-radius: 0; }
  .ad-grid--compact .ad-card__body { padding: var(--space-3); }
}

.ad-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  transition: box-shadow 0.15s, transform 0.15s;
}

.ad-card:hover {
  box-shadow: var(--shadow);
  transform: translateY(-2px);
}

.ad-card--featured { border-color: var(--brand); }

.ad-card__link { color: inherit; display: block; }
.ad-card__link:hover { text-decoration: none; }

/*
 * The photo box.
 *
 * `cover` filled the frame by cropping whatever did not fit, which on a
 * portrait photo means the top and bottom — and the watermark sellers put
 * there was the first thing to go. `contain` shows the picture whole. It
 * leaves a margin on photos whose shape does not match the box, which is the
 * right trade: a letterboxed photo still says what it is, a cropped one has
 * had its label cut off.
 *
 * 3:2 rather than 4:3 because most ad photos are landscape, so the margin is
 * usually nothing, and the box is wide enough at three-across to read a
 * watermark in the corner of it.
 */
/*
 * The photo frame.
 *
 * Two demands that look like they cannot both be met: fill the frame, and crop
 * nothing. `cover` fills it by cutting off whatever does not fit — on a
 * portrait photo that is the top and bottom, where sellers put their watermark.
 * `contain` keeps every pixel but leaves two grey bars beside a photo whose
 * shape does not match.
 *
 * So: the picture is `contain`, and the same picture again — blurred, scaled
 * past the edges — is painted behind it. The frame reads as full, the photo is
 * whole and in its own proportions, and the bars are gone. It is what a photo
 * looks like on a wall, not what it looks like trimmed to fit a slot.
 *
 * The blurred copy is the file already being downloaded, so it costs no
 * request; only a repaint.
 */
.ad-card__media {
  position: relative;
  aspect-ratio: 3 / 2;
  background: var(--surface-sunken);
  overflow: hidden;
}

.ad-card__media::before {
  content: '';
  position: absolute;
  /* Past the edges, so the blur has no soft border of its own. */
  inset: -8%;
  background-image: var(--photo);
  background-size: cover;
  background-position: center;
  filter: blur(14px) saturate(1.15) brightness(0.96);
  transform: scale(1.06);
}

.ad-card__media img {
  position: relative;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* No photo, no backdrop — the placeholder sits on the plain sunken surface. */
.ad-card__media:not([style]) ::before { display: none; }

.ad-card__noimage {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  color: var(--ink-faint);
}

.ad-card__body { padding: var(--space-3); }

.ad-card__title {
  font-size: 0.95rem;
  font-weight: 500;
  margin-bottom: var(--space-2);
  /* Two lines maximum, so cards in a row stay the same height. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.9em;
}

.ad-card__price { font-size: 0.95rem; margin-bottom: var(--space-2); }
.ad-card__price strong { color: var(--ink); }

.ad-card__deposit {
  display: block;
  font-size: 0.8rem;
  color: var(--ink-muted);
}

.ad-card__meta {
  display: flex;
  justify-content: space-between;
  gap: var(--space-2);
  font-size: 0.8rem;
  color: var(--ink-muted);
}

.ad-card__bookmark {
  position: absolute;
  top: var(--space-2);
  inset-inline-start: var(--space-2);
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  background: rgb(255 255 255 / 90%);
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  color: var(--ink-muted);
}

.ad-card__bookmark[aria-pressed='true'] { color: var(--brand-ink); }

.badge {
  position: absolute;
  top: var(--space-2);
  inset-inline-end: var(--space-2);
  padding: 2px var(--space-2);
  border-radius: var(--radius-sm);
  font-size: 0.75rem;
  font-weight: 500;
  color: #fff;
}

.badge--urgent { background: var(--danger); }
.badge--featured { background: var(--brand); color: var(--on-brand); inset-inline-end: auto; inset-inline-start: 44px; }

/* --------------------------------------------------------------- listing */

.listing {
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: var(--space-5);
  align-items: start;
}

.listing__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  flex-wrap: wrap;
}

.listing__title { font-size: 1.25rem; margin-bottom: var(--space-1); }
.listing__count { color: var(--ink-muted); margin: 0; }
.listing__sort { min-width: 160px; }
.listing__more { margin-top: var(--space-5); }

/* -------------------------------------------------------------- ad page */

.ad-page {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: var(--space-5);
  align-items: start;
}

@media (max-width: 899px) {
  .ad-page { grid-template-columns: 1fr; }
}

.ad-page__main {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-4);
}

.gallery__stage {
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--surface-sunken);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: var(--space-2);
}

/* The same trick as the cards: the photo whole, on a blurred copy of itself. */
.gallery__stage::before {
  content: '';
  position: absolute;
  inset: -8%;
  background-image: var(--photo);
  background-size: cover;
  background-position: center;
  filter: blur(22px) saturate(1.15) brightness(0.96);
  transform: scale(1.06);
}

.gallery__stage img {
  position: relative;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.gallery__thumbs {
  display: flex;
  gap: var(--space-2);
  overflow-x: auto;
  padding-bottom: var(--space-1);
}

.gallery__thumb {
  flex: 0 0 auto;
  width: 72px;
  border: 2px solid transparent;
  border-radius: var(--radius-sm);
  overflow: hidden;
  padding: 0;
  background: none;
  cursor: pointer;
}

.gallery__thumb.is-active { border-color: var(--brand); }

/* --------------------------------------------------- moving between photos */

/*
 * Previous and next, on the photo.
 *
 * The thumbnail strip was the only way through, which assumes a mouse and a
 * wide screen. These sit on the stage itself, are a full 44px target, and on a
 * phone they are backed up by a swipe.
 *
 * White on a dark scrim rather than on the photo: a photo can be any colour,
 * and an arrow with no ground behind it disappears against half of them.
 */
.gallery__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  color: #fff;
  background: rgb(16 24 40 / 45%);
  backdrop-filter: blur(4px);
  transition: background 0.15s ease, opacity 0.15s ease;
}

.gallery__nav:hover { background: rgb(16 24 40 / 68%); }
.gallery__nav:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* Physical sides, not logical: "next" is on the left in this layout, and a
   logical property would swap them back. */
.gallery__nav--next { left: var(--space-3); }
.gallery__nav--prev { right: var(--space-3); }

/*
 * Out of the way until wanted, on a device that has a pointer to ask with.
 * Touch devices never produce hover, so there they simply stay visible.
 */
@media (hover: hover) {
  .gallery__nav { opacity: 0; }

  .gallery:hover .gallery__nav,
  .gallery__nav:focus-visible { opacity: 1; }
}

/* Which photo of how many — the thing a strip of thumbnails says implicitly
   and a single photo on a phone does not say at all. */
.gallery__counter {
  position: absolute;
  z-index: 2;
  bottom: var(--space-3);
  left: var(--space-3);
  margin: 0;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: #fff;
  background: rgb(16 24 40 / 55%);
  backdrop-filter: blur(4px);
}

/* A swipe must not also select the picture or trigger the browser's own
   image drag. */
.gallery__stage img {
  user-select: none;
  -webkit-user-drag: none;
  touch-action: pan-y;
}

.ad-head { margin: var(--space-4) 0; }
.ad-head__title { font-size: 1.35rem; }

.ad-head__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  color: var(--ink-muted);
  font-size: 0.9rem;
}

.ad-price {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  padding: var(--space-4);
  background: var(--surface-sunken);
  border-radius: var(--radius);
  margin-bottom: var(--space-4);
}

.ad-price strong { font-size: 1.2rem; }

.spec-list { margin: 0; }

.spec-list__row {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--line);
}

.spec-list dt { color: var(--ink-muted); margin: 0; }
.spec-list dd { margin: 0; }

.prose { white-space: pre-line; word-break: break-word; }

/*
 * The frame is the map, not the section around it.
 *
 * `.ad-map` is the <section>: a heading, this box, and a caption. It used to
 * carry `aspect-ratio: 16/10` and a grey background, which forced the section
 * to a height its contents never reached — the map stopped at 300px and the
 * rest was an empty grey slab under it. The ratio belongs to the tiles.
 *
 * Height by ratio rather than a fixed 300px, with a floor and a ceiling: a
 * map wants to be wide and short on a laptop and nearly square on a phone,
 * and 300px of a 375px-wide screen is neither.
 */
.map {
  aspect-ratio: 16 / 9;
  min-height: 220px;
  max-height: 420px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-sunken);
  /* Leaflet paints tiles into a positioned child; without this the rounded
     corners are drawn under them. */
  isolation: isolate;
}

@media (max-width: 560px) {
  .map { aspect-ratio: 4 / 3; }
}

.ad-actions-secondary {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

.contact-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-4);
  display: grid;
  gap: var(--space-3);
  position: sticky;
  top: calc(var(--header-height) + var(--space-4));
}

.contact-card__seller {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--line);
}

.contact-card__seller strong { display: block; }
.contact-card__seller .avatar { width: 44px; height: 44px; }

.contact-card__phone {
  text-align: center;
  font-size: 1.3rem;
  font-weight: 700;
  letter-spacing: 1px;
  direction: ltr;
  padding: var(--space-3);
  background: var(--brand-light);
  border-radius: var(--radius);
}

.contact-card__warning {
  font-size: 0.8rem;
  color: var(--warning);
  background: #fff8ed;
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  margin: 0;
}

@media (prefers-color-scheme: dark) {
  .contact-card__warning { background: #2a2113; }
  .ad-card__bookmark { background: rgb(20 23 28 / 90%); }
}

.related { margin-top: var(--space-6); }

/* ---------------------------------------------------------------- panels */

.panel {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: var(--space-5);
  align-items: start;
}

@media (max-width: 899px) {
  .panel { grid-template-columns: 1fr; }
}

.panel__nav {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-2);
  position: sticky;
  top: calc(var(--header-height) + var(--space-4));
}

.panel__nav a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--ink);
}

.panel__nav a:hover { background: var(--surface-sunken); text-decoration: none; }
.panel__nav a.is-active { background: var(--brand-light); color: var(--brand-ink); font-weight: 500; }

.panel__nav .count {
  min-width: 20px;
  padding: 0 6px;
  border-radius: 10px;
  background: var(--danger);
  color: #fff;
  font-size: 0.75rem;
  text-align: center;
}

/* ----------------------------------------------------------------- stats */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

.stat {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-4);
}

.stat__value { font-size: 1.5rem; font-weight: 700; }
.stat__label { color: var(--ink-muted); font-size: 0.85rem; }

/* ---------------------------------------------------------------- tables */

.table-wrap { overflow-x: auto; }

.table {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  font-size: 0.9rem;
}

.table th,
.table td {
  padding: var(--space-3);
  text-align: start;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

.table th {
  background: var(--surface-sunken);
  font-weight: 500;
  position: sticky;
  top: 0;
}

.table tbody tr:hover { background: var(--surface-alt); }

/* ----------------------------------------------------------- status pills */

.pill {
  display: inline-block;
  padding: 2px var(--space-2);
  border-radius: 12px;
  font-size: 0.78rem;
  background: var(--surface-sunken);
  color: var(--ink-muted);
}

.pill--published { background: #e6f5ec; color: var(--success); }
.pill--pending_review,
.pill--edited { background: #fff4e5; color: var(--warning); }
.pill--rejected,
.pill--suspended { background: #fdecea; color: var(--danger); }
.pill--awaiting_payment { background: var(--brand-light); color: var(--brand-ink); }
.pill--expired { background: var(--surface-sunken); }

@media (prefers-color-scheme: dark) {
  .pill--published { background: #11301f; }
  .pill--pending_review, .pill--edited { background: #33260f; }
  .pill--rejected, .pill--suspended { background: #331815; }
}

/* --------------------------------------------------------------- flashes */

.flash-stack {
  max-width: var(--container);
  margin: var(--space-3) auto 0;
  padding-inline: var(--space-4);
}

.flash {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius);
  margin-bottom: var(--space-2);
  border-inline-start: 3px solid;
}

.flash--success { background: #e6f5ec; border-color: var(--success); color: var(--success); }
.flash--error { background: #fdecea; border-color: var(--danger); color: var(--danger); }
.flash--warning { background: #fff4e5; border-color: var(--warning); color: var(--warning); }
.flash--info { background: var(--brand-light); border-color: var(--brand); color: var(--brand-ink); }

/* --------------------------------------------------------------- toasts */

.toast-root {
  position: fixed;
  bottom: var(--space-4);
  inset-inline-start: var(--space-4);
  z-index: 90;
  display: grid;
  gap: var(--space-2);
}

.toast {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius);
  background: var(--ink);
  color: #fff;
  box-shadow: var(--shadow-lg);
  max-width: 340px;
  animation: toast-in 0.2s ease-out;
}

.toast--error { background: var(--danger); }
.toast--success { background: var(--success); }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------- dialogs */

.city-dialog {
  border: 0;
  border-radius: var(--radius-lg);
  padding: 0;
  width: min(520px, 92vw);
  max-height: 80vh;
  box-shadow: var(--shadow-lg);
  background: var(--surface);
  color: var(--ink);
}

.city-dialog::backdrop { background: rgb(0 0 0 / 40%); }

/* The step back out of a province, shown only while inside one. */
.city-dialog__back {
  display: block;
  width: 100%;
  padding: var(--space-3) var(--space-4);
  border: 0;
  border-bottom: 1px solid var(--line);
  background: none;
  color: var(--brand-ink);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

/* The ad count beside a province or city, and the province a search hit is in. */
.city-dialog__count,
.city-dialog__where {
  margin-inline-start: auto;
  color: var(--ink-faint);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.city-dialog__where { font-size: 0.7rem; }

.city-dialog__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-4);
  border-bottom: 1px solid var(--line);
}

.city-dialog__head button {
  display: grid;
  place-items: center;
  /* Forty square: a close button that is only as big as the glyph inside it is
     the one control on a dialog that a thumb regularly misses. */
  width: 40px;
  height: 40px;
  margin: -8px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--ink-muted);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

.city-dialog__head button:hover { background: var(--surface-sunken); color: var(--ink); }

.city-dialog__search {
  width: calc(100% - (var(--space-4) * 2));
  margin: var(--space-4);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
}

.city-dialog__body {
  padding: var(--space-2) var(--space-4) var(--space-4);
  overflow-y: auto;
  max-height: 56vh;
}

/*
 * One row per place, not a cloud of chips.
 *
 * A picker is a list to run your eye down — a name, and how many ads are in it,
 * aligned. Laid out as inline pills they wrap into a block of text where no two
 * names start in the same place, which is what made nine hundred cities
 * unreadable.
 */
.city-dialog__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-3);
  border: 0;
  border-bottom: 1px solid #f1f4f8;
  background: none;
  color: var(--ink);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.city-dialog__item:last-child { border-bottom: 0; }

.city-dialog__item:hover,
.city-dialog__item:focus-visible {
  background: var(--surface-alt);
  color: var(--brand-ink);
}

/* ------------------------------------------------------------ empty state */

.empty {
  text-align: center;
  padding: var(--space-7) var(--space-4);
  color: var(--ink-muted);
}

.empty__title { font-size: 1.1rem; color: var(--ink); margin-bottom: var(--space-2); }

/* ----------------------------------------------------------- breadcrumbs */

.breadcrumbs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  list-style: none;
  margin: 0 0 var(--space-4);
  padding: 0;
  font-size: 0.85rem;
  color: var(--ink-muted);
}

.breadcrumbs li::after {
  content: '‹';
  margin-inline-start: var(--space-1);
  color: var(--ink-faint);
}

.breadcrumbs li:last-child::after { content: none; }

/* ----------------------------------------------------------------- footer */

.footer {
  background: var(--surface);
  border-top: 1px solid var(--line);
  margin-top: var(--space-7);
  padding: var(--space-6) var(--space-4) var(--space-4);
}

.footer__grid {
  max-width: var(--container);
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-5);
}

.footer h3 { font-size: 0.95rem; }

.footer ul { list-style: none; margin: 0; padding: 0; }
.footer li { padding: var(--space-1) 0; }
.footer a { color: var(--ink-muted); }

.footer__bottom {
  max-width: var(--container);
  margin: var(--space-5) auto 0;
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
  text-align: center;
  color: var(--ink-faint);
  font-size: 0.85rem;
}

/* ------------------------------------------------------------------- hero */

.hero {
  padding: var(--space-6) 0 var(--space-5);
  text-align: center;
}

.hero__title {
  margin: 0 0 var(--space-2);
  font-size: 1.75rem;
  line-height: 1.35;
  text-wrap: balance;
}

.hero__sub {
  margin: 0 auto var(--space-5);
  max-width: 46ch;
  color: var(--ink-muted);
}

.hero__search {
  display: flex;
  gap: var(--space-2);
  max-width: 620px;
  margin-inline: auto;
}

.hero__search .input {
  flex: 1;
  min-width: 0;
}

.hero__terms {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

/* A term someone actually searched for, offered as a shortcut. */
.chip {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-muted);
  font-size: 0.8125rem;
  text-decoration: none;
  white-space: nowrap;
}

.chip:hover {
  border-color: var(--brand);
  color: var(--brand-ink);
}

@media (min-width: 768px) {
  .hero { padding: var(--space-7) 0 var(--space-6); }
  .hero__title { font-size: 2.25rem; }
}

/* -------------------------------------------------------- home sections */

.home-categories,
.home-latest,
.home-cities {
  margin-top: var(--space-6);
}

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.section-head .section-title { margin: 0; }

.home-cta {
  margin: var(--space-7) 0 var(--space-4);
  padding: var(--space-6) var(--space-5);
  text-align: center;
}

.home-cta h2 { margin: 0 0 var(--space-2); }
.home-cta p { margin: 0 0 var(--space-4); }

/*
 * The category tiles.
 *
 * `auto-fill` rather than a fixed count: the number of tiles per row follows
 * the width, so the same rule serves a phone and a wide desktop without a
 * breakpoint for each.
 */
.category-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: var(--space-3);
}

.category-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-height: 104px;
  padding: var(--space-4) var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  text-align: center;
  text-decoration: none;
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}

.category-tile:hover {
  border-color: var(--brand);
  box-shadow: var(--shadow-sm);
  transform: translateY(-1px);
}

.category-tile__icon {
  color: var(--brand-ink);
  fill: currentcolor;
}

.category-tile__name {
  font-weight: 500;
  line-height: 1.4;
  /* Two lines, then ellipsis — a long category name must not make its tile
     taller than the ones beside it. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.category-tile__count { font-size: 0.75rem; }

/* --------------------------------------------------------- city links */

/*
 * Flat lists of city links: the homepage tail and the picker page. These are
 * the site's internal-linking surface, so they are dense on purpose.
 */
.city-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.city-links a {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.875rem;
  text-decoration: none;
}

.city-links a:hover {
  border-color: var(--brand);
  color: var(--brand-ink);
}

/*
 * --brand-ink, not --brand-dark: #d95f00 on the pale orange ground is 3.42:1,
 * under what a label of this size needs. It went unnoticed because the link
 * used to hold a count in a <span>, and a contrast check that walks leaf text
 * nodes never reached it.
 */
.city-links a.is-current {
  border-color: var(--brand);
  background: var(--brand-light);
  color: var(--brand-ink);
  font-weight: 700;
}

/* The whole country, set apart from the provinces below it. */
.city-links--all {
  margin-bottom: var(--space-5);
  padding-bottom: var(--space-5);
  border-bottom: 1px solid var(--line);
}

.province-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}

.province__name {
  margin: 0 0 var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--line);
  font-size: 1rem;
  color: var(--ink-muted);
}

@media (min-width: 768px) {
  .province-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1100px) {
  .province-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ------------------------------------------------------------ pagination */

.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  margin: var(--space-6) 0 var(--space-4);
}

.pagination a,
.pagination .is-current,
.pagination__gap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 38px;
  height: 38px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.875rem;
  text-decoration: none;
}

.pagination a:hover { border-color: var(--brand); color: var(--brand-ink); }

.pagination .is-current {
  border-color: var(--brand);
  background: var(--brand);
  color: var(--on-brand);
  font-weight: 500;
}

.pagination__gap {
  border-color: transparent;
  background: none;
  color: var(--ink-faint);
}

/* ------------------------------------------------------------------ auth */

.auth {
  display: flex;
  justify-content: center;
  padding: var(--space-6) 0;
}

.auth__card {
  width: 100%;
  max-width: 420px;
  padding: var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}

.auth__title {
  margin: 0 0 var(--space-2);
  font-size: 1.25rem;
  text-align: center;
}

.auth__step { margin-top: var(--space-4); }

/*
 * The code field.
 *
 * Wide tracking and a monospace-ish rhythm so six digits read as six digits;
 * `ltr` because a number typed into an RTL page otherwise reverses as it is
 * entered, which is the single most common complaint about Persian OTP forms.
 */
.auth__code {
  direction: ltr;
  letter-spacing: 0.4em;
  text-align: center;
  font-size: 1.35rem;
  font-variant-numeric: tabular-nums;
}

/*
 * «ارسال مجدد کد» and «تغییر شماره موبایل».
 *
 * `.auth__resend` is the wrapper around the button, not the button — so the
 * rules that were written for it never reached anything clickable, and the
 * disabled state in particular never applied: the countdown ran while the
 * button still looked and read as live.
 *
 * Sized as a real target rather than as text. These two are the way out of a
 * login that has gone wrong — a code that never arrived, a mistyped number —
 * and 24 pixels of underlined text is not something you hit on a phone while
 * holding it one-handed.
 */
.auth__back,
.auth__resend > .linklike {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  margin-top: var(--space-2);
  padding: 0 var(--space-2);
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--brand-ink);
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
}

.auth__back:hover,
.auth__resend > .linklike:hover:not([disabled]) { background: var(--surface-alt); }

.auth__resend > .linklike[disabled] {
  color: var(--ink-faint);
  cursor: default;
  text-decoration: none;
}

.auth__resend { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

.auth__warning {
  margin-top: var(--space-4);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
  color: var(--ink-muted);
  font-size: 0.8125rem;
  line-height: 1.7;
}

/* ------------------------------------------------------------------ blog */

.post-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-4);
}

.post-grid--compact {
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}

.post-grid > * { min-width: 0; }

.post-card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: box-shadow 0.15s, transform 0.15s;
}

.post-card:hover {
  box-shadow: var(--shadow);
  transform: translateY(-2px);
}

.post-card__cover {
  aspect-ratio: 16 / 9;
  width: 100%;
  object-fit: cover;
  background: var(--surface-sunken);
}

.post-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4);
}

.post-card__title {
  margin: 0;
  font-size: 1rem;
  line-height: 1.6;
}

.post-card__meta,
.post__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  color: var(--ink-muted);
  font-size: 0.8125rem;
}

.post__head { margin-bottom: var(--space-5); }

.post__cover {
  width: 100%;
  border-radius: var(--radius);
  margin-bottom: var(--space-5);
}

/* ------------------------------------------------------------------ help */

/*
 * The help centre.
 *
 * Two ways in, because there are two kinds of visitor: one has a question in
 * words and types it, the other is browsing and wants to see what exists. The
 * search answers the first without a round trip; the section cards answer the
 * second by showing what is inside them rather than only naming them.
 */
.help { max-width: 1000px; margin-inline: auto; }

.help-hero {
  padding: var(--space-6) var(--space-5) var(--space-5);
  margin-bottom: var(--space-6);
  border-radius: var(--radius-lg);
  background:
    radial-gradient(120% 140% at 85% 0%, var(--brand-light) 0%, transparent 60%),
    var(--surface-alt);
  border: 1px solid var(--line);
  text-align: center;
}

.help-hero__title {
  margin: 0 0 var(--space-2);
  font-size: clamp(1.35rem, 4vw, 1.9rem);
  text-wrap: balance;
}

.help-hero__lead {
  margin: 0 auto var(--space-5);
  max-width: 46ch;
  color: var(--ink-muted);
  line-height: 1.9;
}

/* --- the search ---------------------------------------------------------- */

.help-search {
  position: relative;
  display: flex;
  max-width: 34rem;
  margin-inline: auto;
}

.help-search__icon {
  position: absolute;
  inset-inline-start: 14px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--ink-faint);
  pointer-events: none;
}

.help-search__input {
  width: 100%;
  min-height: 46px;
  padding: 0 44px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 0.95rem;
}

.help-search__input::placeholder { color: var(--ink-faint); }

.help-search__input:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-light);
}

/* Results replace the browse view while there is something typed. */
.help-results {
  max-width: 34rem;
  margin: var(--space-4) auto 0;
  text-align: start;
}

.help-results__list {
  margin: 0;
  padding: var(--space-1);
  list-style: none;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}

.help-results__list:empty { display: none; }

.help-results__list a {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  color: var(--ink);
  text-decoration: none;
}

.help-results__list a:hover { background: var(--surface-alt); }

.help-results__where { color: var(--ink-faint); font-size: 0.75rem; }

/* What the term matched, marked in the title rather than left to be spotted. */
.help-results__list mark {
  background: var(--brand-light);
  color: var(--brand-ink);
  border-radius: 3px;
  padding: 0 1px;
}

.help-results__empty {
  margin: 0;
  padding: var(--space-4);
  text-align: center;
  color: var(--ink-muted);
}

/* --- the sections -------------------------------------------------------- */

.help-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-4);
}

.help-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.help-card:hover { border-color: var(--line-strong); box-shadow: var(--shadow-sm); }

.help-card__name {
  margin: 0;
  font-size: 1rem;
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--line);
}

.help-card__name a { color: var(--ink); text-decoration: none; }
.help-card__name a:hover { color: var(--brand-ink); }

.help-card__list {
  flex: 1 1 auto;
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.help-card__list a {
  color: var(--ink-muted);
  text-decoration: none;
  font-size: 0.875rem;
  line-height: 1.7;
}

.help-card__list a:hover { color: var(--brand-ink); text-decoration: underline; }

.help-card__more {
  align-self: flex-start;
  color: var(--brand-ink);
  font-size: 0.8125rem;
  text-decoration: none;
}

.help-card__more:hover { text-decoration: underline; }

.help-heading {
  margin: var(--space-7) 0 var(--space-4);
  font-size: 1.125rem;
}

/* --- most read ----------------------------------------------------------- */

/*
 * Numbered because the list *is* a ranking — these are the eight articles
 * people actually open. A number that encoded nothing would be decoration.
 */
.help-rank {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: 1fr;
  gap: 2px;
}

@media (min-width: 720px) {
  .help-rank { grid-template-columns: 1fr 1fr; column-gap: var(--space-5); }
}

.help-rank__item {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--line);
}

.help-rank__n {
  flex: 0 0 auto;
  width: 1.25rem;
  font-size: 0.875rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--brand-ink);
}

.help-rank__link { color: var(--ink); text-decoration: none; line-height: 1.7; }
.help-rank__link:hover { color: var(--brand-ink); text-decoration: underline; }

/* --- the questions ------------------------------------------------------- */

.faq-list {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}

.faq { border-bottom: 1px solid var(--line); }
.faq:last-child { border-bottom: 0; }

.faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4);
  cursor: pointer;
  font-weight: 500;
  list-style: none;
}

.faq__q::-webkit-details-marker { display: none; }
.faq__q:hover { background: var(--surface-alt); }
.faq[open] .faq__q { background: var(--surface-alt); }

/*
 * A plus that becomes a minus. Two bars, one of which is rotated away when the
 * question opens — the state is visible without reading the answer.
 */
.faq__mark {
  position: relative;
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
}

.faq__mark::before,
.faq__mark::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  top: 6px;
  height: 2px;
  border-radius: 1px;
  background: var(--ink-faint);
  transition: transform 0.18s ease;
}

.faq__mark::after { transform: rotate(90deg); }
.faq[open] .faq__mark::after { transform: rotate(0deg); }
.faq[open] .faq__mark::before,
.faq[open] .faq__mark::after { background: var(--brand-ink); }

.faq__a { padding: 0 var(--space-4) var(--space-4); }

/* --- a section page ------------------------------------------------------ */

.help-head { margin-bottom: var(--space-5); }
.help-head__title { margin: 0 0 var(--space-2); font-size: 1.5rem; }
.help-head__lead { margin: 0; color: var(--ink-muted); line-height: 1.9; }

.help-articles {
  margin: 0;
  padding: 0;
  list-style: none;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}

.help-articles li { border-bottom: 1px solid var(--line); }
.help-articles li:last-child { border-bottom: 0; }

.help-article-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 52px;
  padding: var(--space-3) var(--space-4);
  color: var(--ink);
  text-decoration: none;
  line-height: 1.7;
}

.help-article-row:hover { background: var(--surface-alt); color: var(--brand-ink); }

/* Physical borders: a logical one would point sideways in RTL. */
.help-article-row__go {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-left: 2px solid var(--ink-faint);
  border-bottom: 2px solid var(--ink-faint);
  transform: rotate(45deg);
}

.help-article-row:hover .help-article-row__go { border-color: var(--brand); }

/* --- one article --------------------------------------------------------- */

.help-article__title { margin: var(--space-2) 0 var(--space-4); font-size: 1.5rem; }
.help-article__body .prose-html { max-width: 68ch; }

@media (min-width: 900px) {
  .help-article__side { position: sticky; top: calc(var(--header-height) + var(--space-4)); }
}

.help-sidelist {
  margin: 0 0 var(--space-3);
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.help-sidelist a {
  color: var(--ink-muted);
  text-decoration: none;
  font-size: 0.875rem;
  line-height: 1.7;
}

.help-sidelist a:hover { color: var(--brand-ink); text-decoration: underline; }

.help-side-ask .btn { margin-top: var(--space-3); }

.helpful {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
  color: var(--ink-muted);
  font-size: 0.875rem;
}

.helpful__buttons { display: flex; gap: var(--space-2); }

/* --- the way out --------------------------------------------------------- */

.help-ask {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-7);
  padding: var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-alt);
}

.help-ask__title { margin: 0 0 var(--space-1); font-size: 1rem; }
.help-ask > div { min-width: 0; }
.help-ask .muted { margin: 0; }

@media (max-width: 560px) {
  .help-hero { padding: var(--space-5) var(--space-4); }
  .help-ask { flex-direction: column; align-items: stretch; }
  .help-ask .btn { width: 100%; }
}

/* ----------------------------------------------------------- static pages */

/*
 * Body copy the site does not author: imported page and article HTML. It
 * arrives with whatever markup the old editor produced, so the rules here are
 * defensive — a measure, a floor under the line height, and a ceiling on
 * anything that could be wider than the column.
 */
.static-page,
.prose-html {
  max-width: 72ch;
  line-height: 2;
}

.prose-html h2,
.prose-html h3 { margin: var(--space-6) 0 var(--space-3); line-height: 1.5; }
.prose-html p { margin: 0 0 var(--space-4); }
.prose-html ul,
.prose-html ol { margin: 0 0 var(--space-4); padding-inline-start: var(--space-5); }
.prose-html li { margin-bottom: var(--space-2); }
.prose-html img { max-width: 100%; height: auto; border-radius: var(--radius-sm); }
.prose-html a { color: var(--brand-ink); }

/*
 * Colours the old editor wrote into the content itself are ignored.
 *
 * Pages imported from the previous site carry inline styles — a heading in
 * pure red, a note in light grey — chosen against a white page that no longer
 * exists. Pure red on white is 4.0:1, under what body text needs, and in dark
 * mode those same colours are worse. Emphasis survives: it is carried by
 * <strong> and by size, which work in both themes.
 */
.prose-html [style*="color"] { color: inherit !important; }
.prose-html [style*="background"] { background: none !important; }
.prose-html table { width: 100%; border-collapse: collapse; }
.prose-html td,
.prose-html th { border: 1px solid var(--line); padding: var(--space-2); }

/* A table in imported copy can be wider than the column; it scrolls, not the page. */
.prose-html { overflow-x: auto; }

.lead {
  font-size: 1.0625rem;
  color: var(--ink-muted);
  line-height: 1.9;
}

.link-list { margin: 0; padding: 0; list-style: none; }
.link-list li { padding: var(--space-2) 0; border-bottom: 1px solid var(--line); }
.link-list li:last-child { border-bottom: 0; }

/* ------------------------------------------------------------ error pages */

/*
 * 404, 403 and 500.
 *
 * These had markup and no stylesheet at all — the views named
 * `.error-page__code` and friends and nothing ever defined them, so the page
 * fell back to a bare stack of default blocks with the two links touching.
 *
 * They matter more here than on most sites: the old URLs were de-indexed and
 * changed shape, so a 404 is a page real visitors arrive on from an old link
 * or an expired ad, not a dead end nobody sees. It gets the one thing that
 * turns a dead end into a route onward — a search box — and reads as part of
 * the site rather than as the server having given up.
 */
.error-page {
  max-width: 34rem;
  margin: var(--space-7) auto;
  padding: var(--space-6) var(--space-5);
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

/*
 * The number, as a mark rather than as a sentence.
 *
 * Quiet on purpose: it is the least useful thing on the page for the person
 * reading it, so it sets the scene and then gets out of the way of the
 * heading, which is the part that says what happened.
 */
.error-page__code {
  margin: 0 0 var(--space-3);
  font-size: clamp(3rem, 12vw, 4.5rem);
  font-weight: 700;
  line-height: 1;
  /*
   * --brand-ink, not --brand: #ff7700 on white is 2.66:1, under the 3:1 even
   * display-sized text needs. The bright orange is for fills with dark type
   * on top of it, which is why the button below this reads and this would
   * not have.
   */
  color: var(--brand-ink);
  letter-spacing: 0.02em;
}

.error-page h1 {
  margin: 0 0 var(--space-3);
  font-size: 1.375rem;
  text-wrap: balance;
}

.error-page > .muted {
  margin: 0 auto var(--space-5);
  max-width: 30ch;
  line-height: 1.9;
}

/* Input and button on one line, with the input taking whatever is left. */
.error-page__search {
  display: flex;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}

.error-page__search .input { flex: 1 1 auto; min-width: 0; }
.error-page__search .btn { flex: 0 0 auto; }

.error-page__links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
}

.error-page__links .btn { flex: 0 1 auto; }

/*
 * The trace, in development only.
 *
 * Left-to-right and scrolling inside its own box: a stack trace is neither
 * Persian nor narrow, and without this it decides the width of the page.
 */
.error-page__debug {
  margin-top: var(--space-6);
  text-align: start;
  border-top: 1px solid var(--line);
  padding-top: var(--space-4);
}

.error-page__debug summary {
  cursor: pointer;
  color: var(--ink-muted);
  font-size: 0.875rem;
}

.error-page__debug pre {
  margin-top: var(--space-3);
  padding: var(--space-3);
  overflow-x: auto;
  font-size: 0.75rem;
  line-height: 1.7;
  background: var(--surface-sunken);
  border-radius: var(--radius-sm);
}

@media (max-width: 520px) {
  .error-page {
    margin: var(--space-5) 0;
    padding: var(--space-5) var(--space-4);
  }

  /* Two full-width controls side by side leave no room for either label. */
  .error-page__search { flex-direction: column; }
  .error-page__links .btn { flex: 1 1 100%; }
}

/* ----------------------------------------------------------------- seals */

/*
 * The licence seals.
 *
 * A seal is a claim about who you are, so it has to look issued rather than
 * decorative: a real frame, the name of the body that issued it, and a visible
 * way through to their verification page. Two logos floating centred on a page
 * — which is how they arrived from the old site — read as clip art.
 */
.seals {
  margin-top: var(--space-7);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
}

.seals__title { margin: 0 0 var(--space-2); font-size: 1.125rem; }
.seals__lead { margin: 0 0 var(--space-4); line-height: 1.9; }

/*
 * Two across wherever they fit.
 *
 * The page they sit on is a reading column — 72ch, about 570px — so the floor
 * is set low enough that two cards still fit inside it. Above 280px each they
 * would drop to one per row and two seals would read as a list of unrelated
 * things rather than as a pair.
 */
.seals__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(228px, 1fr));
  gap: var(--space-3);
}

/*
 * Stacked, not side by side inside the card.
 *
 * These sit in a 72ch reading column, so a card is about 280px wide. Put the
 * seal beside the text in that and «نماد اعتماد الکترونیکی» breaks over two
 * lines with the issuer over three, in a column narrow enough that every line
 * ends mid-phrase. Stacked, the text gets the whole width and the card reads
 * as what it is — a certificate with its name under it.
 */
.seal {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.seal:hover {
  border-color: var(--brand);
  box-shadow: var(--shadow-sm);
}

.seal:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/*
 * A white tile behind the seal, in both themes.
 *
 * Both seals are drawn for a white page — the eNamad one is a PNG with a pale
 * ground and dark type. On the dark theme they would sit on near-black and
 * become unreadable, so the tile stays white and the border does the work of
 * separating it from the page.
 */
.seal__frame {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 104px;
  height: 104px;
  padding: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #fff;
  overflow: hidden;
}

/*
 * `contain`, and a floor under the height.
 *
 * eNamad serve their seal at whatever size their generator returns, and if
 * their server is unreachable the image collapses to nothing and the tile
 * folds up around the alt text. The fixed tile above keeps the row the same
 * shape either way.
 */
.seal__frame img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  /* The alt text, when the seal cannot be fetched. */
  font-size: 0.6875rem;
  line-height: 1.6;
  color: #5b6472;
  text-align: center;
}

.seal__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  min-width: 0;
}

.seal__name { font-size: 0.9375rem; line-height: 1.7; }

.seal__issuer {
  font-size: 0.8125rem;
  line-height: 1.7;
  color: var(--ink-muted);
}

.seal__verify {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-1);
  font-size: 0.8125rem;
  color: var(--brand-ink);
}

.seal:hover .seal__verify { text-decoration: underline; }

/* Physical borders: a logical one points sideways once the page is RTL. */
.seal__go {
  width: 6px;
  height: 6px;
  border-left: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.15s ease, margin 0.15s ease;
}

.seal:hover .seal__go { margin-inline-end: 3px; }

.seal__name { text-wrap: balance; }

/* The verify line sits apart from the naming, with a rule above it. */
.seal__verify {
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--line);
  width: 100%;
  justify-content: center;
}

/* -------------------------------------------------------------- two-col */

.two-col {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}

.two-col > * { min-width: 0; }

@media (min-width: 900px) {
  .two-col { grid-template-columns: minmax(0, 1fr) 320px; }
  .two-col--wide { grid-template-columns: minmax(0, 1fr) 380px; }
}

/* ------------------------------------------------------------ ad page */

.ad-page__side {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.ad-price__main {
  font-size: 1.375rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.ad-price__deposit {
  color: var(--ink-muted);
  font-size: 0.9375rem;
  font-variant-numeric: tabular-nums;
}

.ad-specs {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--space-3);
  margin: 0;
}

.ad-specs > * { min-width: 0; }

/* The section is a section — heading, map, caption — like every other one. */
.ad-map {
  margin-top: var(--space-5);
}

.ad-map > .muted {
  margin: var(--space-2) 0 0;
}

/* --------------------------------------------------------- listing extras */

.listing__results { min-width: 0; }

/* The filter drawer's close control, shown only while the drawer is open. */
.filter-back {
  display: none;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  margin-bottom: var(--space-4);
  padding: var(--space-3);
  border: 0;
  border-bottom: 1px solid var(--line);
  background: none;
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}

@media (max-width: 899px) {
  .listing__filters.is-open .filter-back { display: flex; }
}

/* ------------------------------------------------------- payment result */

.payment-result {
  max-width: 520px;
  margin: var(--space-6) auto;
  padding: var(--space-6) var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  text-align: center;
}

.payment-result__actions,
.empty__actions,
.dialog__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
  margin-top: var(--space-5);
}

.dialog__actions { justify-content: flex-end; }

.empty__tips {
  margin: var(--space-4) auto 0;
  max-width: 44ch;
  color: var(--ink-muted);
  font-size: 0.875rem;
  text-align: start;
}

.report-dialog { max-width: 460px; }

/* ------------------------------------------------------------ footer note */

.footer__warning {
  margin-top: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-inline-start: 3px solid var(--warning);
  background: var(--surface-sunken);
  color: var(--ink-muted);
  line-height: 1.8;
}

/* ------------------------------------------------------- the daily chart */

/*
 * Views per day, on the owner's stats page.
 *
 * Bars rather than a line: each value is a count for one day, not a reading of
 * something continuous, and a day with no views has to be visibly a gap rather
 * than a line resting on the axis.
 */
.stats-chart-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.stats-chart-head .section-title { margin: 0; }
.stats-chart-head__note { margin: 0; }

/* The scale down one side, the plot filling the rest. */
/*
 * Two columns and two rows: the scale beside the plot, the dates beneath it.
 * The dates sit in the plot's own column so "a month ago" lands under the
 * first bar rather than under the scale numbers.
 */
.stats-chart {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto auto;
  column-gap: var(--space-3);
  align-items: stretch;
}

.stats-chart__grid { grid-column: 1; grid-row: 1; }
.stats-chart__plot { grid-column: 2; grid-row: 1; }
.stats-chart__axis { grid-column: 2; grid-row: 2; }

.stats-chart__grid {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  text-align: end;
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
  /* Half a line, so each number sits on its rule rather than under it. */
  padding-block: 0;
  margin-block: -0.5em;
}

.stats-chart__plot {
  width: 100%;
  height: 180px;
  display: block;
  overflow: visible;
}

.stats-chart__rule {
  stroke: var(--line);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.stats-chart__bar {
  fill: var(--brand);
  transition: fill 0.15s ease;
}

.stats-chart__bar:hover { fill: var(--brand-dark); }

/*
 * A day with nothing still gets a sliver, in the line colour rather than the
 * brand one — so the row of days reads as continuous without a zero pretending
 * to be a small number.
 */
.stats-chart__bar--empty { fill: var(--line); }

.stats-chart__axis {
  display: flex;
  justify-content: space-between;
  margin-top: var(--space-2);
  font-size: 0.75rem;
  color: var(--ink-faint);
}

@media (max-width: 560px) {
  .stats-chart__plot { height: 140px; }
}

/* ------------------------------------------------------------- my ads */

/*
 * The owner's own list of ads.
 *
 * Every class here was named in the template and defined nowhere, so the page
 * rendered as a bare stack: cover photos stretched to whatever height the row
 * gave them, no card around anything, and «حذف» wrapping onto its own line
 * hard against the row above — a delete button with no gap under the finger.
 *
 * Same shape as an ad card on the site: photo, then what the ad says, then
 * what you can do to it.
 */
.my-ads {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.my-ad {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.my-ad:hover { border-color: var(--line-strong); box-shadow: var(--shadow-sm); }

/* A fixed square, so a portrait photo and a panorama make the same shape. */
.my-ad__media {
  position: relative;
  aspect-ratio: 1;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-sunken);
}

.my-ad__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.my-ad__body { min-width: 0; }

.my-ad__title {
  margin: 0 0 var(--space-2);
  font-size: 1rem;
  line-height: 1.6;
}

.my-ad__title a { color: var(--ink); text-decoration: none; }
.my-ad__title a:hover { color: var(--brand-ink); }

/*
 * The two fact rows. `gap` on a wrapping flex row rather than margins, so the
 * separators never end up doubled at a line break.
 */
.my-ad__meta,
.my-ad__stats {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-3);
  margin: 0;
}

.my-ad__stats {
  margin-top: var(--space-2);
  font-variant-numeric: tabular-nums;
}

/* A dot between the facts, drawn rather than typed, and never leading. */
.my-ad__stats span + span::before {
  content: '·';
  margin-inline-end: var(--space-3);
  color: var(--line-strong);
}

/*
 * The actions: their own row under the body, wrapping with a real gap.
 *
 * The delete button sits inside a <form>, which is a block — that is what put
 * it on its own line with nothing between it and the row above. Making the
 * form a flex item of the same row puts it back in line with the others and
 * gives it the same gap as everything else.
 */
.my-ad__actions {
  grid-column: 2;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}

.my-ad__actions form { display: contents; }
.my-ad__actions .btn { flex: 0 0 auto; }

/*
 * The status chip is already defined above, near the flashes — it is shared
 * with the admin lists. `needs_review` was the one status missing a colour,
 * so it fell back to plain grey next to «در انتظار بررسی», which is the same
 * thing to the reader.
 */
.pill--needs_review { background: #fff4e5; color: var(--warning); }

@media (max-width: 560px) {
  .my-ad {
    grid-template-columns: 72px minmax(0, 1fr);
    gap: var(--space-3);
    padding: var(--space-3);
  }

  /* Full width under the card, where a thumb can reach them all. */
  .my-ad__actions { grid-column: 1 / -1; }
  .my-ad__actions .btn { flex: 1 1 auto; min-height: 40px; }
}

/* ---------------------------------------------------------------- timeline */

/*
 * The status history on an ad's stats page.
 *
 * These rules lived only in admin.css, which the member panel does not load —
 * so the owner's own timeline rendered as a bare bulleted list.
 */
.timeline { list-style: none; margin: 0; padding: 0; }

.timeline li {
  position: relative;
  padding: var(--space-2) 0;
  padding-inline-start: var(--space-4);
  border-inline-start: 2px solid var(--line);
}

.timeline li::before {
  content: '';
  position: absolute;
  inset-inline-start: -5px;
  top: 14px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--line-strong);
}

.timeline li:first-child::before { background: var(--brand); }

/* -------------------------------------------------------------- utilities */

/* Hidden, but still in the document — JS toggles this rather than inline styles. */
.hide { display: none !important; }

.ltr { direction: ltr; text-align: start; }
.icon { width: 1em; height: 1em; fill: currentcolor; vertical-align: -0.125em; }

/* A control that reads as a link: same colour, no button chrome. */
.linklike {
  border: 0;
  background: none;
  padding: 0;
  color: var(--brand-ink);
  font: inherit;
  cursor: pointer;
  text-decoration: underline;
}

/* ------------------------------------------------------------- first visit */

/*
 * The page a new visitor lands on. One question — which city — and the page is
 * shaped so the answer is never more than a glance and a tap away: the busiest
 * ten as chips, a search box for anyone who knows their city's name, and the
 * full list folded away by province underneath.
 */
.welcome {
  max-width: 760px;
  margin: 0 auto;
  padding: var(--space-6) 0 var(--space-7);
  text-align: center;
}

.welcome__title {
  margin: 0 0 var(--space-2);
  font-size: 1.6rem;
  line-height: 1.4;
}

.welcome__sub {
  margin: 0 auto var(--space-5);
  max-width: 44ch;
  color: var(--ink-muted);
  font-size: 0.9375rem;
}

.welcome__search { margin: 0 0 var(--space-5); }

.welcome__input {
  width: 100%;
  padding: var(--space-3) var(--space-4);
  font-size: 1rem;
  text-align: center;
}

.welcome__popular {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
  margin-bottom: var(--space-6);
}

.welcome__chip {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: 9px 16px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  font-weight: 500;
  text-decoration: none;
  transition: border-color 0.15s, background 0.15s;
}

.welcome__chip:hover {
  border-color: var(--brand);
  background: var(--brand-light);
  color: var(--brand-ink);
}

.welcome__chip-count {
  color: var(--ink-faint);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.welcome__provinces {
  text-align: start;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}

.welcome__province { border-bottom: 1px solid var(--line); }
.welcome__province:last-child { border-bottom: 0; }

.welcome__province summary {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
  list-style: none;
  font-weight: 500;
}

.welcome__province summary::-webkit-details-marker { display: none; }

/* A chevron that turns, so the fold reads as a fold. */
.welcome__province summary::after {
  content: '';
  width: 7px;
  height: 7px;
  margin-inline-start: auto;
  /* Physical borders: a logical one swaps sides with the writing direction and
     turns the chevron sideways. */
  border-right: 2px solid var(--ink-faint);
  border-bottom: 2px solid var(--ink-faint);
  transform: rotate(45deg);
  transition: transform 0.15s;
}

.welcome__province[open] summary::after { transform: rotate(-135deg); }
.welcome__province[open] summary { background: var(--surface-alt); }
.welcome__province summary:hover { background: var(--surface-alt); }

.welcome__province-count {
  color: var(--ink-faint);
  font-size: 0.8125rem;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}

.welcome__cities {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 1px;
  padding: var(--space-2) var(--space-3) var(--space-4);
  background: var(--surface);
}

.welcome__cities > * { min-width: 0; }

.welcome__cities a {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-size: 0.875rem;
  text-decoration: none;
}

.welcome__cities a:hover { background: var(--brand-light); color: var(--brand-ink); }
.welcome__cities a .muted { margin-inline-start: auto; }

.welcome__all {
  margin-top: var(--space-5);
  font-size: 0.9375rem;
}

/* While the search box has something in it, the folds are opened and anything
   that does not match is hidden — typing searches every city at once. */
.welcome__province.is-hidden,
.welcome__cities a.is-hidden { display: none; }

@media (min-width: 640px) {
  .welcome { padding-top: var(--space-7); }
  .welcome__title { font-size: 2rem; }
}

/* ------------------------------------------------------------- where I am */

/* The "find my city" button on the chooser, beside the box you would type in. */
.welcome__locate {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-3) 0 var(--space-5);
}

.welcome__locate-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 40px;
  padding: 0 var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.welcome__locate-btn:hover { border-color: var(--brand); background: var(--brand-light); }
.welcome__locate-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.welcome__locate-btn .icon { color: var(--brand-ink); }

.welcome__locate-note:empty { display: none; }

/* --- the suggestion above a listing -------------------------------------- */

/*
 * Only ever shown to somebody who has already allowed location once, so it is
 * an offer rather than an interruption: quiet ground, one action, one way to
 * make it go away for good.
 */
.geo-suggest {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-alt);
  font-size: 0.875rem;
}

.geo-suggest__text { flex: 1 1 auto; min-width: 0; color: var(--ink); line-height: 1.7; }

.geo-suggest__accept {
  flex: 0 0 auto;
  min-height: 34px;
  padding: 0 var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--brand);
  color: var(--on-brand);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 700;
  cursor: pointer;
}

.geo-suggest__accept:hover { background: var(--brand-dark); }
.geo-suggest__accept:focus-visible { outline: 2px solid var(--brand-ink); outline-offset: 2px; }

.geo-suggest__dismiss {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--ink-muted);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}

.geo-suggest__dismiss:hover { background: var(--surface-sunken); color: var(--ink); }
.geo-suggest__dismiss:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

@media (max-width: 480px) {
  /* The two controls share a line under the sentence rather than squeezing
     onto it — at this width the city name is what gets clipped otherwise. */
  .geo-suggest__text { flex: 1 1 100%; }
}

/* ------------------------------------------------------------- categories */

/* --------------------------------------------- where you are in the tree */

.cat-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin-bottom: var(--space-3);
  font-size: 0.8125rem;
}

.cat-nav__up {
  color: var(--brand-ink);
  text-decoration: none;
}

.cat-nav__up:hover { text-decoration: underline; }

/* A separator drawn rather than typed, so it is not read aloud. */
.cat-nav__up + .cat-nav__up::before,
.cat-nav__up + .cat-nav__here::before {
  content: '‹';
  margin-inline: 4px;
  color: var(--ink-faint);
}

.cat-nav__here {
  font-weight: 700;
  color: var(--ink);
}

.cat-nav__up.is-current { font-weight: 700; color: var(--ink); }

/* ------------------------------------------------ the sidebar refine list */

/*
 * Rows, with the count on the far side and a whole-row target. The old version
 * padded by four pixels and put the count immediately after the name, so two
 * categories with names of different length had their numbers in different
 * places and neither the names nor the numbers could be read down the column.
 */
.filter-list a {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 9px var(--space-2);
  margin-inline: calc(var(--space-2) * -1);
  border-radius: var(--radius-sm);
  color: var(--ink);
  text-decoration: none;
  font-size: 0.875rem;
}

.filter-list a:hover,
.filter-list a:focus-visible {
  background: var(--brand-light);
  color: var(--brand-ink);
}

.filter-list__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.filter-list__count {
  margin-inline-start: auto;
  color: var(--ink-faint);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.filter-list a:hover .filter-list__count { color: inherit; opacity: 0.75; }

@media (max-width: 899px) {
  /* In the drawer these are the primary target, so they get thumb-sized. */
  .filter-list a { padding-block: 13px; font-size: 0.9375rem; }
}

/* --------------------------------------------------------- filter panel */

/*
 * The filters, on the desktop sidebar and inside the drawer on a phone.
 *
 * Most of the class names here were in the markup and defined nowhere, which
 * is why the panel looked the way it did: the two price boxes stacked full
 * width because their row was a plain block, and the sections ran into each
 * other because `.filter-group` had no spacing of its own.
 */
.filters {
  display: flex;
  flex-direction: column;
}

.filter-group {
  padding-block: var(--space-4);
  border-top: 1px solid var(--line);
}

/* The first group sits under the panel's own title, which already has a rule. */
.filter-group:first-of-type {
  padding-top: var(--space-2);
  border-top: 0;
}

.filter-group__title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--ink);
}

/* «تومان» belongs to the field, not to the heading — so it is quieter. */
.filter-group__unit {
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--ink-faint);
}

/* --- the price range ----------------------------------------------------- */

.filter-range {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: end;
  gap: 6px;
}

.filter-range__field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.filter-range__label {
  font-size: 0.75rem;
  color: var(--ink-muted);
}

/*
 * The label stays put. A placeholder disappears the moment someone types, and
 * a filled-in pair of boxes then says nothing about which end is which.
 */
/*
 * Sized to the number, not to the box.
 *
 * The sidebar gives each of these about a hundred pixels, and a formatted
 * ۹۰٬۰۰۰٬۰۰۰ is eleven characters — at the body size it ran past the edge and
 * the leading digits scrolled out of sight, which is the one thing a price
 * field must never do.
 */
.filter-range__input {
  width: 100%;
  min-width: 0;
  padding-inline: 8px;
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

.filter-range__input::placeholder {
  color: var(--ink-faint);
  font-size: 0.8125rem;
}

/* A short rule between the two, sitting on the inputs' own line. */
.filter-range__dash {
  width: 6px;
  height: 1px;
  margin-bottom: 18px;
  background: var(--line-strong);
}

/* --- one of three -------------------------------------------------------- */

.segmented {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-alt);
}

.segmented__option {
  position: relative;
  display: flex;
  min-width: 0;
}

/*
 * The radio itself is still there and still focusable — it is what the form
 * submits and what a screen reader announces. Only its box is hidden.
 */
.segmented__option input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}

.segmented__option span {
  flex: 1 1 auto;
  min-width: 0;
  padding: 7px 4px;
  border-radius: var(--radius-sm);
  font-size: 0.8125rem;
  line-height: 1.6;
  text-align: center;
  color: var(--ink-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background 0.15s ease, color 0.15s ease;
}

.segmented__option:hover span { color: var(--ink); }

.segmented__option input:checked + span {
  background: var(--surface);
  color: var(--ink);
  font-weight: 700;
  box-shadow: var(--shadow-sm);
}

.segmented__option input:focus-visible + span {
  outline: 2px solid var(--brand);
  outline-offset: -2px;
}

/* --- a single yes/no ----------------------------------------------------- */

/*
 * A whole-row target with the box at the reading edge, so it lines up with
 * everything else in the column instead of drifting to the far side of a
 * label whose length happens to differ.
 */
.filter-check {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 40px;
  margin-inline: calc(var(--space-2) * -1);
  padding-inline: var(--space-2);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.filter-check:hover { background: var(--surface-alt); }

.filter-check input {
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  margin: 0;
  accent-color: var(--brand);
  cursor: pointer;
}

.filter-check__label {
  font-size: 0.875rem;
  line-height: 1.7;
  color: var(--ink);
}

/* --- apply and clear ----------------------------------------------------- */

.filter-actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-2);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

/* How many are on, on the button that turns them off. */
.filter-actions__count {
  display: inline-grid;
  place-items: center;
  min-width: 18px;
  height: 18px;
  margin-inline-start: var(--space-2);
  padding-inline: 5px;
  border-radius: 999px;
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
  background: var(--brand-light);
  color: var(--brand-ink);
}

@media (max-width: 899px) {
  /* Inside the drawer there is room to breathe, and thumbs to accommodate. */
  .filter-group { padding-block: var(--space-5); }
  .filter-range__input { min-height: 44px; font-size: 0.9375rem; }
  .segmented__option span { padding-block: 10px; font-size: 0.875rem; }
  .filter-check { min-height: 48px; }
  .filter-check input { width: 20px; height: 20px; }
}

/* ----------------------------------------------------- the listing column */

/*
 * Categories and filters stack in one column beside the results. They are two
 * panels because they answer two different questions — which list, and how much
 * of it — and reading them as one made the category list look like a filter
 * nobody had to use.
 */
.listing__side {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  position: sticky;
  top: calc(var(--header-height) + var(--space-4));
}

.cat-panel,
.listing__filters {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-4);
}

/* The heading and close button a panel only needs while it is a drawer. */
.panel-drawer__head {
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin: calc(var(--space-4) * -1) calc(var(--space-4) * -1) var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--line);
}

.panel-drawer__title { margin: 0; font-size: 1rem; }

.panel-drawer__close {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  margin: -8px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  font-size: 1.6rem;
  line-height: 1;
  color: var(--ink-muted);
  cursor: pointer;
  padding: 0;
}

.panel-drawer__close:hover { background: var(--surface-sunken); color: var(--ink); }

/* ----------------------------------------------------- the category panel */

/*
 * The heading each panel shows while it is a column.
 *
 * The drawer head above carries the same words on a phone, where the panel
 * covers the screen and needs a close button beside them. On a desktop that
 * head is hidden, and without this the category list starts with no label at
 * all — a column of names that could be anything.
 */
.cat-panel > .panel-title,
.listing__filters .panel-title {
  margin: 0 0 var(--space-3);
  font-size: 0.9rem;
  font-weight: 700;
}

.cat-list {
  list-style: none;
  margin: 0;
  padding: 0;
  /*
   * Its own scroll, not the page's.
   *
   * Twenty-eight departments is longer than the filters beside it and longer
   * than most first screens. Letting the column grow to fit them puts the
   * filters below the fold and leaves a long empty rail next to the results
   * once you scroll past. Capped, the panel stays a panel.
   */
  max-height: min(52vh, 460px);
  overflow-y: auto;
  overscroll-behavior: contain;
  margin-inline: calc(var(--space-2) * -1);
  padding-inline: var(--space-2);
}

.cat-list a {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 9px var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--ink);
  text-decoration: none;
  font-size: 0.875rem;
}

.cat-list a:hover,
.cat-list a:focus-visible {
  background: var(--brand-light);
  color: var(--brand-ink);
}

.cat-list__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cat-list__count {
  margin-inline-start: auto;
  color: var(--ink-faint);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.cat-list a:hover .cat-list__count { color: inherit; opacity: 0.75; }

.cat-panel__empty { margin: 0; }

/* -------------------------------------------------- the results header */

.listing__headings { min-width: 0; }

.listing__tools {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* The two drawer buttons, hidden where there is a column instead. */
.listing__drawer-btn {
  display: none;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
}

.listing__drawer-btn:hover { border-color: var(--brand); color: var(--brand-ink); }

/*
 * The sort box.
 *
 * A bare <select> is drawn by the operating system and looks like nothing else
 * on the page — different border, different height, its own arrow in the wrong
 * corner for a right-to-left page. This gives it the same frame as everything
 * beside it and draws the arrow where the text ends.
 */
.listing__sort { position: relative; display: inline-flex; }

.listing__sort-select {
  appearance: none;
  padding: 8px 14px;
  padding-inline-start: 34px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
}

.listing__sort-select:hover { border-color: var(--brand); }

.listing__sort-select:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}

/*
 * The chevron, drawn rather than typed.
 *
 * Its *position* follows the writing direction — inset-inline-start puts it at
 * the end of the text either way. Its *shape* must not: a box with a logical
 * border rotated 45 degrees points down in one direction and sideways in the
 * other, because the logical border swaps sides. The borders here are physical
 * for that reason, and it points down in both.
 */
.listing__sort::after {
  content: '';
  position: absolute;
  inset-inline-start: 14px;
  top: 50%;
  width: 7px;
  height: 7px;
  margin-top: -5px;
  border-right: 2px solid var(--ink-faint);
  border-bottom: 2px solid var(--ink-faint);
  transform: rotate(45deg);
  pointer-events: none;
}

/* ------------------------------------------------------------ the sort box */

/*
 * Once the script has taken over, the native control is gone from view but
 * still in the page — it is the value that gets submitted with no script, and
 * the list the popup is built from.
 */
.listing__sort--enhanced .listing__sort-select { display: none; }
.listing__sort--enhanced::after { display: none; }

.sortbox { position: relative; }

.sortbox__trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 38px;
  padding: 0 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.sortbox__trigger:hover { border-color: var(--line-strong); box-shadow: var(--shadow-sm); }
.sortbox__trigger:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

.sortbox.is-open .sortbox__trigger {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-light);
}

.sortbox__icon { color: var(--ink-faint); flex: 0 0 auto; }
.sortbox__label { white-space: nowrap; }

/*
 * Physical borders, rotated. A logical border swaps sides in RTL, which turns
 * a chevron pointing down into one pointing sideways.
 */
.sortbox__chevron {
  width: 7px;
  height: 7px;
  margin-top: -3px;
  border-right: 2px solid var(--ink-faint);
  border-bottom: 2px solid var(--ink-faint);
  transform: rotate(45deg);
  transition: transform 0.18s ease, margin 0.18s ease;
}

.sortbox.is-open .sortbox__chevron {
  transform: rotate(-135deg);
  margin-top: 3px;
}

.sortbox__list {
  position: absolute;
  z-index: 30;
  top: calc(100% + 6px);
  inset-inline-end: 0;
  min-width: 100%;
  padding: var(--space-1);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.sortbox__option {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  min-height: 36px;
  padding: 0 10px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: 0.875rem;
  text-align: start;
  white-space: nowrap;
  cursor: pointer;
}

.sortbox__option:hover { background: var(--surface-alt); }
.sortbox__option:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

/* The chosen one, marked by a tick drawn in the brand colour rather than by a
   filled blue bar the operating system chose. */
.sortbox__option[aria-selected="true"] {
  color: var(--brand-ink);
  font-weight: 700;
  background: var(--brand-light);
}

.sortbox__option[aria-selected="true"]::before {
  content: '';
  flex: 0 0 auto;
  width: 5px;
  height: 9px;
  margin-inline-end: 2px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
}

/* Room for the tick on the options that do not have one, so the labels line
   up rather than shifting sideways as the selection moves. */
.sortbox__option[aria-selected="false"] { padding-inline-start: 19px; }

@media (max-width: 719px) {
  .sortbox__trigger { min-height: 40px; }
  .sortbox__option { min-height: 44px; }
}

/* --------------------------------------------------------------- a phone */

@media (max-width: 899px) {
  .listing { grid-template-columns: 1fr; }

  /* No column: each panel becomes a drawer of its own. */
  .listing__side { display: contents; }

  .cat-panel,
  .listing__filters {
    position: fixed;
    inset: 0;
    z-index: 60;
    border: 0;
    border-radius: 0;
    padding: var(--space-4);
    background: var(--surface);
    overflow-y: auto;
    overscroll-behavior: contain;
    transform: translateX(100%);
    transition: transform 0.2s ease;
  }

  [dir='ltr'] .cat-panel,
  [dir='ltr'] .listing__filters { transform: translateX(-100%); }

  .cat-panel.is-open,
  .listing__filters.is-open { transform: none; }

  /* The page behind a full-screen drawer must not scroll with it. */
  body.has-drawer { overflow: hidden; }

  .panel-drawer__head { display: flex; }

  .panel-title { display: none; }

  .listing__drawer-btn { display: inline-flex; }

  /* Thumb-sized inside a drawer, where these are the only targets. */
  .cat-list a { padding-block: 13px; font-size: 0.9375rem; }
  .cat-list { max-height: none; }

  .listing__head { gap: var(--space-2); }

  /*
   * Three controls on one line at 375px.
   *
   * They came to 336px of content inside 343px of room, which fits until the
   * two gaps are counted and then it is nine pixels over — so the sort dropped
   * to a line of its own and the ads moved down a row for it. Trimmed rather
   * than allowed to wrap: a control that wraps on one phone and not another is
   * the kind of thing that looks broken without ever being wrong.
   */
  .listing__tools { width: 100%; flex-wrap: nowrap; gap: 6px; }
  .listing__sort { margin-inline-start: auto; min-width: 0; }

  .listing__drawer-btn { padding-inline: 11px; }

  .listing__sort-select {
    padding-inline-end: 10px;
    padding-inline-start: 28px;
    max-width: 100%;
  }

  .listing__sort::after { inset-inline-start: 10px; }
}

/* ------------------------------------------------- the card on a phone */

/*
 * One ad per row, laid out sideways.
 *
 * Two small cards side by side is a contact sheet: the photo is too small to
 * tell one folding chair from another and the title is cut off at three words.
 * A full-width row gives the photo a fixed square at one end and the whole
 * remaining width to the title, price and place — which is the shape every
 * classifieds app on a phone has converged on, because it is the one that lets
 * you decide without opening anything.
 */
@media (max-width: 719px) {
  .ad-grid {
    grid-template-columns: 1fr;
    gap: 0;
    border-top: 1px solid var(--line);
  }

  .ad-card {
    border: 0;
    border-bottom: 1px solid var(--line);
    border-radius: 0;
    background: none;
  }

  .ad-card:hover { box-shadow: none; transform: none; }

  .ad-card__link {
    display: grid;
    grid-template-columns: 104px minmax(0, 1fr);
    gap: var(--space-3);
    align-items: start;
    padding: var(--space-3) var(--space-1);
  }

  .ad-card__media {
    aspect-ratio: 1;
    border-radius: var(--radius-sm);
    overflow: hidden;
  }

  .ad-card__body {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 0;
    min-width: 0;
  }

  .ad-card__title {
    font-size: 0.9375rem;
    line-height: 1.6;
    /* Two lines is enough to tell two ads apart and keeps every row the same
       height, so the eye can run down the prices. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  .ad-card__price { font-size: 0.9375rem; }

  .ad-card__meta {
    margin-top: auto;
    font-size: 0.75rem;
  }

  /* The badges belong on the photo, which is now small — so they shrink. */
  .ad-card .badge {
    font-size: 0.625rem;
    padding: 1px 5px;
  }

  .ad-card__bookmark {
    top: var(--space-3);
    inset-inline-start: var(--space-1);
  }
}

/* ------------------------------------------- grid and flex items may shrink */

/*
 * A grid or flex item defaults to `min-width: auto`, which means it refuses to
 * become narrower than its own content. One wide thing inside — a long unbroken
 * URL, a table, a row of thumbnails — and the track grows to fit it, taking the
 * page with it. The result is a page that scrolls sideways on a phone, and the
 * element responsible is never the one that looks wrong.
 *
 * This is not a tidy-up. On the ad page it was pushing the document to 2,922px
 * wide inside a 375px viewport: the whole layout off-screen, with a blank
 * column where the content should have been.
 *
 * Every two-track layout in the project gets the same permission. Applied to
 * the children rather than globally, because `min-width: 0` on everything also
 * removes the floor from things that need one.
 */
.ad-page > *,
.listing > *,
.panel > *,
.spec-list__row > *,
.ad-grid > *,
.category-grid > *,
.post-grid > *,
.help-grid > *,
.ad-specs > *,
.two-col > *,
.stat-grid > *,
.admin > *,
.admin-page > *,
.field-row > * {
  min-width: 0;
}

/*
 * The same guard one level down, where the content actually arrives: anything
 * that holds text a user typed can contain a 200-character unbroken string,
 * and a URL pasted into an ad description is the usual one.
 */
.ad-description,
.ad-description *,
.post__body,
.post__body *,
.help-article,
.help-article *,
.message-body,
.bubble p,
.thread__preview,
.spec-list__row dd {
  overflow-wrap: anywhere;
}

/*
 * Last line of defence. If something still overflows, the page must not be the
 * thing that scrolls — a sideways-scrolling document is the single most broken
 * thing a phone user can meet, and it hides the fix rather than showing it.
 */
html, body { overflow-x: clip; }

/* ------------------------------------------------------------------- chat */

/*
 * A conversation is the one screen here that behaves like an app rather than a
 * document: the header and composer stay put and only the middle scrolls, so
 * the send box is reachable without hunting for it on a phone.
 */

.chat {
  display: grid;
  grid-template-rows: auto 1fr auto auto;
  height: calc(100vh - var(--header-height) - var(--space-6));
  max-height: 780px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

.chat__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}

.chat__back {
  font-size: 1.6rem;
  line-height: 1;
  color: var(--ink-muted);
  text-decoration: none;
  padding: 0 var(--space-2);
}

.chat__ad {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
  color: inherit;
  text-decoration: none;
}

.chat__ad img {
  width: 40px;
  height: 30px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  flex: none;
}

.chat__ad strong {
  display: block;
  font-size: 0.92rem;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat__messages {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4);
  overflow-y: auto;
  background: var(--surface-alt);
  overscroll-behavior: contain;
}

.bubble {
  align-self: flex-start;
  max-width: min(74%, 520px);
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius) var(--radius) var(--radius) var(--radius-sm);
  box-shadow: var(--shadow-sm);
}

/* Mine sit on the other side, with the tail mirrored to match. */
.bubble--mine {
  align-self: flex-end;
  background: var(--brand-light);
  border-color: transparent;
  border-radius: var(--radius) var(--radius) var(--radius-sm) var(--radius);
}

.bubble p {
  margin: 0;
  font-size: 0.94rem;
  line-height: 1.75;
  overflow-wrap: anywhere;
}

.bubble__time {
  display: block;
  margin-top: var(--space-1);
  font-size: 0.72rem;
  color: var(--ink-faint);
}

.bubble__file {
  display: block;
  margin-bottom: var(--space-2);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.bubble__file img {
  display: block;
  max-width: 100%;
  /* Capped so one tall photo cannot push the rest of the conversation off
     screen; the full size is a click away. */
  max-height: 260px;
  object-fit: cover;
  background: var(--surface-sunken);
}

.bubble__file-name {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--surface-sunken);
  border-radius: var(--radius-sm);
  font-size: 0.88rem;
  color: var(--brand-ink);
}

.bubble__file-name::before { content: '📎'; }

.chat__composer {
  display: flex;
  align-items: flex-end;
  gap: var(--space-2);
  padding: var(--space-3);
  border-top: 1px solid var(--line);
  background: var(--surface);
}

.chat__composer textarea {
  flex: 1;
  resize: none;
  /* One line to start; chat.js grows it with the text up to this ceiling.
     The shared textarea.input rule is sized for a form field, which in a
     composer leaves a box four lines tall waiting for a one-line message. */
  min-height: 40px;
  height: 40px;
  max-height: 140px;
  padding: var(--space-2) var(--space-3);
  line-height: 1.6;
}

.chat__attach {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--ink-muted);
  cursor: pointer;
}

.chat__attach:hover { background: var(--surface-sunken); color: var(--brand-ink); }

/* The file input is visually hidden, so its focus ring has to land on the
   label that stands in for it. */
.chat__composer input[type="file"]:focus-visible + label,
.chat__attach:focus-within {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}

.chat__pending {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--line);
  background: var(--surface-alt);
  font-size: 0.85rem;
  color: var(--ink-muted);
}

.chat__pending span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  direction: ltr;
  text-align: right;
}

.chat__pending::before { content: '📎'; }

.chat__warning,
.chat__blocked {
  margin: 0;
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink-faint);
  font-size: 0.8rem;
  text-align: center;
}

@media (max-width: 720px) {
  .chat {
    height: calc(100dvh - var(--header-height));
    max-height: none;
    border: 0;
    border-radius: 0;
  }
}

/* ------------------------------------------------------------------ print */

@media print {
  .header, .footer, .contact-card, .ad-actions-secondary, .listing__filters { display: none; }
  body { background: #fff; }
  .ad-page { grid-template-columns: 1fr; }
}

/* ========================================================== member panel ==
 *
 * The panel had a shell and almost nothing else.
 *
 * `layouts/panel.php` extends `layouts/web.php`, which loads this file and
 * only this file — admin.css is never on the page. But the panel's markup had
 * been written against class names that live in admin.css, or against names
 * that were never written anywhere at all: forty-two of them across every
 * screen past the dashboard. The status tabs, the category picker that opens
 * posting an ad, the photo grid, the chat list, the ticket thread, the service
 * picker on the page that takes payment — each was a run of naked inline
 * elements.
 *
 * Everything below is the missing half. Nothing here is new design: it follows
 * the tokens and the shapes the public side already uses, so the panel reads
 * as the same site rather than as a second one.
 */

/* The column beside the nav. Without a min-width a wide table inside it made
   the grid track grow and pushed the nav off the screen. */
.panel__body {
  min-width: 0;
}

/* ------------------------------------------------------------------ tabs */

/*
 * The status filter. `.tabs`/`.tab` existed only in admin.css, so this bar was
 * a run-on line of links with no selected state — «همه ۳۷ در انتظار پرداخت ۱
 * در حال بررسی ۱» read as one sentence.
 */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-1);
  border-bottom: 1px solid var(--line);
}

.tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  color: var(--ink-muted);
  font-size: 0.875rem;
  text-decoration: none;
  white-space: nowrap;
  /* A transparent underline at rest so selecting one does not move the row. */
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.tab:hover {
  background: var(--surface-alt);
  color: var(--ink);
  text-decoration: none;
}

.tab.is-active {
  border-bottom-color: var(--brand);
  color: var(--brand-ink);
  font-weight: 500;
}

/* The count rides along quietly; it is a detail of the tab, not a second label. */
.tab .muted {
  font-variant-numeric: tabular-nums;
  font-size: 0.8125rem;
}

/* --------------------------------------------------- dashboard callouts */

/*
 * "One ad is waiting for payment" — the most valuable line on the dashboard,
 * and it was rendering as a bare orange link above the tiles.
 */
.action-items {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

.action-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: 1 1 220px;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-inline-start: 3px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.action-item:hover {
  box-shadow: var(--shadow-sm);
  text-decoration: none;
}

.action-item__count {
  flex: 0 0 auto;
  min-width: 2rem;
  font-size: 1.375rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.action-item__title {
  font-size: 0.9375rem;
  line-height: 1.6;
}

/* The severity lives on the edge, not in the whole fill: four bright cards in
   a row would each be shouting and none of them would be heard. */
.action-item--warning { border-inline-start-color: var(--warning); }
.action-item--warning .action-item__count { color: var(--warning); }
.action-item--danger { border-inline-start-color: var(--danger); }
.action-item--danger .action-item__count { color: var(--danger); }
.action-item--muted .action-item__count { color: var(--ink-muted); }

/* A note under a figure — "since the ad was posted", "over 30 days". */
.stat__note { margin-top: var(--space-1); line-height: 1.6; }
.stat__value--quiet { color: var(--ink-faint); }

/* --------------------------------------------------- posting: categories */

/*
 * The first screen of posting an ad. It was a paragraph of orange links with
 * the price of each category run together after its name.
 */
.category-picker {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.category-picker__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 52px;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.category-picker__item:hover {
  border-color: var(--brand);
  background: var(--brand-light);
  text-decoration: none;
}

.category-picker__item:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* The chevron points the way reading travels, which in RTL is left. */
.category-picker__item > .muted[aria-hidden] {
  font-size: 1.25rem;
  line-height: 1;
  color: var(--ink-faint);
}

/* --------------------------------------------------------- posting: form */

.ad-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* The asterisk on a required label: red enough to find, small enough to ignore. */
.required {
  color: var(--danger);
  font-weight: 700;
  margin-inline-start: 2px;
}

/*
 * The banner above the edit form. Its whole job is to say what state the ad is
 * in, and it was three unstyled lines of text.
 */
.status-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
  margin-bottom: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-inline-start: 3px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface-alt);
}

.status-banner strong { flex: 0 0 auto; }
.status-banner p { flex: 1 1 260px; margin: 0; color: var(--ink-muted); line-height: 1.7; }
.status-banner .btn { flex: 0 0 auto; }

.status-banner--published { border-inline-start-color: var(--success); background: #f2faf5; }
.status-banner--awaiting_payment { border-inline-start-color: var(--brand); background: var(--brand-light); }
.status-banner--pending_review,
.status-banner--needs_review,
.status-banner--edited { border-inline-start-color: var(--warning); background: #fffaf2; }
.status-banner--rejected,
.status-banner--suspended { border-inline-start-color: var(--danger); background: #fdf4f3; }

/* The save row, separated from the last field so it does not read as part of it. */
.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  padding-top: var(--space-2);
}

/* ------------------------------------------------------- posting: photos */

.media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: var(--space-3);
  margin-top: var(--space-3);
}

.media-item {
  position: relative;
  margin: 0;
  aspect-ratio: 4 / 3;
  border: 2px solid transparent;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-sunken);
}

.media-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Which photo the listing will show. Said twice — a ring and a word — because
   a coloured border alone is not a label. */
.media-item.is-cover { border-color: var(--brand); }

.media-item.is-cover::after {
  content: 'عکس اصلی';
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  padding: 2px var(--space-2);
  background: var(--brand);
  color: var(--on-brand);
  font-size: 0.6875rem;
  font-weight: 700;
  text-align: center;
}

.media-item__actions {
  position: absolute;
  inset-block-start: var(--space-1);
  inset-inline-end: var(--space-1);
  display: flex;
  gap: var(--space-1);
}

.media-item__actions button {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgb(0 0 0 / 55%);
  color: #fff;
  font-size: 0.875rem;
  line-height: 1;
  cursor: pointer;
}

.media-item__actions button:hover { background: rgb(0 0 0 / 78%); }
.media-item__actions button:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
.media-item__actions [data-delete-media]:hover { background: var(--danger); }

/* The add button is the same shape as a photo, so the grid stays a grid. */
.media-upload {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 3;
  border: 2px dashed var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-alt);
  color: var(--ink-muted);
  font-size: 0.875rem;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.media-upload:hover { border-color: var(--brand); color: var(--brand-ink); }
.media-upload:focus-within { outline: 2px solid var(--brand); outline-offset: 2px; }

/* ------------------------------------------------------- my ads: extras */

/* The placeholder for an ad with no photo, matching the public card's. */
.my-ad__noimage {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  color: var(--ink-faint);
}

/* ---------------------------------------------------------- buying a promotion */

/*
 * The service picker.
 *
 * This is the page that takes money, and it rendered as one wrapped paragraph
 * with every checkbox, name, description and price run together — there was no
 * way to tell which price belonged to which service.
 */
.service-option {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--space-3);
  align-items: start;
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-bottom: var(--space-2);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.service-option:hover { border-color: var(--line-strong); background: var(--surface-alt); }

/* Selected is a state worth seeing at a glance when several are ticked. */
.service-option:has(input:checked) {
  border-color: var(--brand);
  background: var(--brand-light);
}

.service-option:has(input:focus-visible) {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.service-option input { margin-top: 4px; }

.service-option__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.service-option__name { font-size: 0.9375rem; }
.service-option__note { line-height: 1.7; }

.service-option__price {
  white-space: nowrap;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* What the ticked services come to, before the buyer agrees to it. */
.promote-total {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin: var(--space-4) 0;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius);
  background: var(--surface-sunken);
  font-size: 1.0625rem;
}

.promote-total strong {
  font-size: 1.375rem;
  font-variant-numeric: tabular-nums;
  color: var(--brand-ink);
}

/* ---------------------------------------------------------------- wallet */

.wallet-balance {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.wallet-balance__value {
  font-size: 1.75rem;
  font-variant-numeric: tabular-nums;
}

.preset-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.preset-row .btn { font-variant-numeric: tabular-nums; }

/*
 * Credit and debit, which lived only in admin.css — so on the member's own
 * statement a top-up and a purchase were the same colour.
 */
.amount--in { color: var(--success); font-variant-numeric: tabular-nums; }
.amount--out { color: var(--danger); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------------ chat */

.thread-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.thread {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) auto;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.thread:hover { border-color: var(--line-strong); box-shadow: var(--shadow-sm); text-decoration: none; }
.thread.is-unread { border-inline-start: 3px solid var(--brand); background: var(--brand-light); }

.thread__body { min-width: 0; }

.thread__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

/* Three lines that must each stay on one line: a long ad title used to push
   the timestamp off the card. */
.thread__ad,
.thread__preview {
  margin: var(--space-1) 0 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.thread__preview { font-size: 0.875rem; color: var(--ink-muted); }
.thread.is-unread .thread__preview { color: var(--ink); font-weight: 500; }

/* --------------------------------------------------------------- tickets */

.ticket-thread {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin: var(--space-4) 0;
}

/*
 * Whose message this is, said by where it sits and what it sits on. Support's
 * replies were visually identical to the member's own, in a screen whose only
 * purpose is a conversation between the two.
 */
.ticket-message {
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  max-width: 88%;
  margin-inline-start: auto;
}

.ticket-message--staff {
  margin-inline-start: 0;
  margin-inline-end: auto;
  border-color: var(--brand);
  background: var(--brand-light);
}

.ticket-message__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}

/* `prose` already sets pre-line, so nl2br on top of it doubled every break. */
.ticket-message .prose { white-space: normal; line-height: 1.8; }

.ticket-message__file {
  display: inline-block;
  margin-top: var(--space-2);
  font-size: 0.875rem;
}

/* ------------------------------------------------------------- profile */

.avatar--large {
  width: 96px;
  height: 96px;
  font-size: 2rem;
}

.avatar-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
}

/* --------------------------------------------------------- odds and ends */

/* A bookmarked ad that is no longer published: present, and plainly not live. */
.ad-card--inactive {
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface-alt);
  opacity: 0.85;
}

.ad-card--inactive .ad-card__body { padding: var(--space-4); }

/* A button that should read as a link — used for "send" in a chat composer. */
.btn-link {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: var(--brand-ink);
  cursor: pointer;
  text-decoration: underline;
}

/* `.linklike.danger` — the delete control on a saved search. */
.linklike.danger,
.danger { color: var(--danger); }

/* The scale numbers beside the views chart. */
.stats-chart__tick {
  font-size: 0.75rem;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.stats-chart__caption { margin: 0 0 var(--space-3); }

/* The status history: a reason and a note are different things and had been
   running together into one sentence. */
.timeline__item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-2);
}

.timeline__reason { color: var(--ink); }
.timeline__note { flex: 1 1 100%; font-size: 0.875rem; line-height: 1.7; }

/* ---------------------------------------------------------- ad preview */

/*
 * The bar above an owner's preview of their own ad.
 *
 * Sticky, because the page below it is the full ad and the reader has to be
 * able to tell at any scroll position that this is not the live page.
 */
.preview-bar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--brand);
  border-radius: var(--radius);
  background: var(--brand-light);
}

.preview-bar__text {
  flex: 1 1 260px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.preview-bar__text span { color: var(--ink-muted); font-size: 0.875rem; line-height: 1.7; }

@media (max-width: 640px) {
  .service-option {
    grid-template-columns: auto minmax(0, 1fr);
  }

  /* The price drops under the name rather than squeezing the description into
     a two-character column. */
  .service-option__price {
    grid-column: 2;
    justify-self: start;
  }

  .ticket-message { max-width: 100%; }

  .thread { grid-template-columns: 40px minmax(0, 1fr); }
}

/* The quiet "I'm done" control under a ticket's reply box. */
.ticket-close { margin-top: var(--space-3); text-align: center; }

/* The "what happens next" line beside the save button. */
.form-actions__note { flex: 1 1 100%; margin: 0; line-height: 1.7; }

/* --------------------------------------------- panel: the last few gaps --
 *
 * Found by checking every class the panel's markup uses against this file
 * rather than by looking at the screens — these are the ones whose only
 * definition was in admin.css, which the panel never loads, plus the status
 * modifiers the pill and banner families were missing.
 */

/* The unread badge on a conversation row. `.panel__nav .count` is scoped to
   the sidebar, so inside .thread the number rendered bare. */
.thread .count {
  min-width: 20px;
  padding: 0 6px;
  border-radius: 10px;
  background: var(--danger);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 20px;
  text-align: center;
}

/* Two fields on one line — نام / نام خانوادگی on the profile form, which
   stacked because only the min-width guard existed here. */
.field-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-3);
}

/* The two statuses the pill family never covered. A draft and a deleted ad
   both reach the panel — ownedAd() gates on deleted_at, not on status. */
.pill--draft { background: var(--surface-sunken); color: var(--ink-muted); }
.pill--deleted { background: #fdecea; color: var(--danger); }

/* Same three for the edit page's banner. */
.status-banner--draft { border-inline-start-color: var(--line-strong); }
.status-banner--expired { border-inline-start-color: var(--ink-faint); background: var(--surface-alt); }
.status-banner--deleted { border-inline-start-color: var(--danger); background: #fdf4f3; }

/* --- sign-in temporarily closed ----------------------------------------- */

/* Shown in place of the form while there is no way to deliver a code. */
.auth__closed {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-alt);
  text-align: center;
}

.auth__closed-icon { color: var(--warning); }
.auth__closed-text { margin: 0; line-height: 1.9; color: var(--ink); }
.auth__closed .btn { margin-top: var(--space-2); }
