/* Reusable, screen-agnostic component styles. */

/* ---------------------------------------------------------------------
   App bar (top header)
--------------------------------------------------------------------- */
.app-bar {
  /* LAYOUT: not position:fixed any more. The header is the FIRST CHILD of the
     scrolling <main>, so it scrolls away together with the content instead of
     floating over it. The negative margins cancel <main>'s side/top padding so
     the bar still spans the full width, flush with the top edge. */
  position: relative;
  flex: none;
  z-index: 1;
  margin-inline: calc(-1 * var(--margin));
  margin-top: 0;
  width: auto;
  /* Grows downward by the safe-area inset (fullscreen mode has no native
     status bar to hold the clock/battery clear of our header anymore, so
     we pad for it ourselves) instead of centering content into a fixed
     height that would otherwise sit under it. */
  height: calc(var(--header-height) + var(--tg-safe-area-top));
  padding-top: var(--tg-safe-area-top);
  /* (removed `max-width` + `margin: 0 auto`: those centred the old fixed bar;
     here they would override the negative side margins above and shrink the
     header to its content width.) */
  align-self: stretch;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline: var(--margin);
  /* Transparent: header isn't fixed/floating (it scrolls away with the
     content, see the LAYOUT note above), so it doesn't need its own bar
     color to stand apart from what's under it — it just sits on the page
     background, same as everything else in <main>. */
  background: transparent;
  /* The divider that separates the header from the page body now lives
     on its OWN element (.app-bar__divider, rendered right after this
     bar — see template() in select-service.js) instead of as this bar's
     own border-bottom: a border here sits flush under the logo/avatar
     with no breathing room, and stretches edge-to-edge with no inset.
     The separate element can be nudged down and inset from the sides
     instead. */
  box-shadow: none;
}
/* Sits a bit below the header (not flush against it) and stops short of
   both edges instead of spanning full-width — reads as a deliberate,
   tidy divider rather than the header's own flat border. Darkened and
   given more breathing room above/below — the previous 12px/10px margins
   still read as the header and the service switcher right under it
   running together, so both gaps are bigger now and the line itself is
   a touch darker/thicker to actually register as a separator. */
.app-bar__divider {
  height: 1.5px;
  margin: 18px 24px 16px;
  background: rgba(15, 23, 42, 0.22);
}

.app-bar__icon-btn {
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  color: var(--c-on-surface-variant);
  transition: opacity 0.15s ease;
}
.app-bar__icon-btn:active {
  opacity: 0.7;
}

.app-bar__title-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.app-bar__title {
  font: var(--text-headline-sm);
  color: var(--c-on-surface);
  line-height: 1.2;
}
.app-bar__logo {
  /* was 40px tall / 220px max, then 52px — bumped again for more presence
     in the header. */
  height: 64px;
  width: auto;
  max-width: 280px;
  object-fit: contain;
}

/* Brand mark shown alone in the header (see public/assets/logo-mark*.png). */
.app-bar__logo-mark {
  width: 44px;
  height: 44px;
  object-fit: contain;
  flex-shrink: 0;
}
.app-bar__subtitle {
  font: var(--text-label-sm);
  color: var(--c-on-surface-variant);
  display: flex;
  align-items: center;
  gap: 4px;
  font-weight: 500;
}
.app-bar__subtitle-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-full);
  background: var(--c-tertiary);
  display: inline-block;
}

/* Right-aligned brand block used on the select-service header (brand name
   + tiny Persian subtitle stacked, RTL-start aligned) plus the instant
   -delivery pill on the opposite side. */
.app-bar__brand {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: right;
}
.app-bar__brand-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.app-bar__brand-title {
  font-weight: 700;
  font-size: 15px;
  color: var(--c-on-surface);
  letter-spacing: -0.01em;
  line-height: 1.2;
}
.app-bar__brand-subtitle {
  font-size: 12px;
  font-weight: 500;
  color: var(--c-on-surface-variant);
  margin-top: 1px;
  line-height: 1;
}
.app-bar__delivery-badge {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: var(--radius-full);
  font-size: 11px;
  font-weight: 600;
  color: #059669;
  background: rgba(16, 185, 129, 0.1);
  border: 1px solid rgba(52, 211, 153, 0.25);
}
.app-bar__delivery-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-full);
  background: #10b981;
  margin-inline-start: 6px;
  animation: pulse-dot 1.8s ease-in-out infinite;
}
@keyframes pulse-dot {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

/* ---------------------------------------------------------------------
   Security note pill (below the header on the select-service screen)
--------------------------------------------------------------------- */
.security-note {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  text-align: center;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.75) 0%, rgba(255, 255, 255, 0.45) 100%);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-full);
  padding: 8px 16px;
  box-shadow: 0 4px 16px 0 rgba(31, 38, 135, 0.04);
}
.security-note__icon {
  width: 14px;
  height: 14px;
  color: #f59e0b;
  flex-shrink: 0;
}
.security-note__text {
  font: var(--text-label-sm);
  color: var(--c-on-surface-variant);
  font-weight: 600;
}

/* ---------------------------------------------------------------------
   Main scroll canvas
--------------------------------------------------------------------- */
.app-main {
  flex: 1;
  min-height: 0; /* required so a flex child can actually shrink and scroll instead of stretching #app taller than the viewport */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding-inline: var(--margin);
  /* Header is the first child INSIDE this scroller and sits flush at the top,
     so no top padding: the flex `gap` below already spaces it from the content. */
  padding-top: 0;
  padding-bottom: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
/* Bottom clearance so the last row of content can scroll fully ABOVE the
   fixed bottom bar(s). It is a real flex item (not padding-bottom) on
   purpose: trailing padding of a scroll container is dropped from the
   scrollable area by some WebViews, a spacer element never is.
   Screens override --main-clearance (confirm/result set a fixed value; the
   select screen measures its two floating docks at runtime — see
   js/screens/select-service.js). The default is >= 120px. */
.app-main::after {
  content: "";
  display: block;
  flex: 0 0 auto;
  height: var(--main-clearance, calc(120px + var(--tg-safe-area-bottom)));
}

/* ---------------------------------------------------------------------
   Step progress indicator
--------------------------------------------------------------------- */
.step-progress {
  padding-top: var(--space-xs);
  padding-bottom: var(--space-xs);
}
.step-progress__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline: 4px;
  margin-bottom: var(--space-sm);
}
.step-progress__label {
  font: var(--text-label-sm);
  color: var(--c-primary);
  font-weight: 700;
}
.step-progress__percent {
  font: var(--text-label-sm);
  color: var(--c-secondary);
  font-weight: 500;
}
.step-progress__bars {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
}
/* The wizard is 4 steps: select -> recipient -> confirm & pay -> result.
   (The old standalone "در حال انتقال به درگاه" screen was folded into the
   confirm step's پرداخت button, and the recipient-username field was split
   out of "select" into its own screen — see recipient.js.) The base
   .step-progress__bars above is already a 4-column grid, so no override is
   needed for the 4-step wizard; --3 is kept only in case some screen still
   wants a 3-bar layout. */
.step-progress__bars--3 {
  grid-template-columns: repeat(3, 1fr);
}
.step-progress__bar {
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--c-surface-container-highest);
}
/* Two shades of the same blue: steps you have already PASSED are the pale
   blue, the step you are ON is the strong deep blue, upcoming steps stay grey. */
.step-progress__bar--done {
  background: #bae6fd;
}
.step-progress__bar--active {
  background: #0284c7;
  box-shadow: 0 0 0 1px rgba(2, 132, 199, 0.15), 0 2px 8px -2px rgba(2, 132, 199, 0.55);
}

/* ---------------------------------------------------------------------
   Segmented control (tabs)
--------------------------------------------------------------------- */
.segmented {
  display: flex;
  align-items: center;
  background: var(--c-surface-container-high);
  border: 1px solid color-mix(in srgb, var(--c-outline-variant) 60%, transparent);
  border-radius: var(--radius-xl);
  padding: 4px;
  gap: 4px;
}
.segmented__btn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 12px;
  border-radius: var(--radius-lg);
  font: var(--text-label-md);
  font-weight: 700;
  color: var(--c-secondary);
  transition: background-color 0.15s ease, color 0.15s ease;
}
.segmented__btn--active {
  background: var(--select-bg);
  color: var(--c-primary);
  border-color: transparent;
}
.segmented__icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

/* ---------------------------------------------------------------------
   Card (generic container)
--------------------------------------------------------------------- */
.card {
  /* PERF: no backdrop-filter here. Cards sit on a near-flat pastel backdrop, so
     the blur showed nothing but cost a full re-blur every scroll frame on weak
     phones. Slightly higher alpha keeps the identical frosted look. */
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.94) 0%, rgba(255, 255, 255, 0.8) 100%);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-2xl);
  padding: var(--space-lg);
  box-shadow: var(--glass-shadow);
}
.card__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: 12px;
  margin-bottom: 12px;
  border-bottom: 1px solid var(--c-surface-container-high);
}
.card__row:last-child {
  border-bottom: none;
  margin-bottom: 0;
  padding-bottom: 0;
}
.card__label {
  font: var(--text-body-sm);
  color: var(--c-secondary);
  font-weight: 500;
}
.card__value {
  font: var(--text-body-md);
  color: var(--c-on-surface);
  font-weight: 600;
}

/* ---------------------------------------------------------------------
   Quantity stepper
--------------------------------------------------------------------- */
.stepper {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  background: #f7f8fb;
  border: 1px solid rgba(15, 23, 42, 0.07);
  border-radius: 18px;
  /* A bit roomier than before — the stars panel's content (stepper +
     presets) read noticeably smaller/denser than the premium and gift
     panels next to it, which have a big image or a 2-column card grid
     to fill the same space. */
  padding: 8px;
  box-shadow: none;
}
.stepper__btn {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #ffffff;
  border: 1px solid rgba(15, 23, 42, 0.08);
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--c-on-surface-variant);
  transition: background-color 0.15s ease, transform 0.1s ease;
  flex-shrink: 0;
}
.stepper__btn:active {
  transform: scale(0.92);
  background: rgba(100, 116, 139, 0.12);
}
.stepper__btn--plus {
  background: var(--c-primary-container, #0098ea);
  border-color: transparent;
  box-shadow: 0 2px 6px rgba(0, 152, 234, 0.3);
  color: #fff;
}
.stepper__btn--plus:active {
  background: var(--c-primary-container, #0098ea);
  filter: brightness(0.92);
}
.stepper__display {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0;
  flex: 1;
  min-width: 0;
}
.stepper__value-row {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: text;
  border-radius: var(--radius-md);
  padding: 2px 8px;
  transition: background-color 0.15s ease;
}
.stepper__value-row:hover,
.stepper__value-row:focus-visible {
  background: rgba(0, 152, 234, 0.08);
  outline: none;
}
.stepper__value {
  font: var(--text-headline-lg-mobile);
  font-size: 22px;
  line-height: 1.25;
  font-weight: 800;
  color: var(--c-on-surface);
  letter-spacing: -0.01em;
  min-width: 3ch;
  text-align: center;
}
/* "انتخاب کنید" shown before the user has picked any quantity (preset tap,
   manual type, or +/-) — lighter weight/colour than a real chosen number
   so it reads as a prompt, not as if 0/something were already selected. */
.stepper__value--placeholder {
  font-weight: 700;
  color: var(--c-on-surface-variant);
  font-size: 0.85em;
}
/* Same visual weight as .stepper__value while active, so swapping the
   read-only span for this input doesn't shift layout. */
.stepper__value-input {
  font: var(--text-headline-lg-mobile);
  font-size: 22px;
  line-height: 1.25;
  font-weight: 800;
  color: var(--c-on-surface);
  letter-spacing: -0.01em;
  min-width: 5ch;
  width: 6ch;
  text-align: center;
  border: none;
  border-bottom: 2px solid var(--c-primary);
  background: transparent;
  outline: none;
  padding: 0;
  -moz-appearance: textfield;
}
.stepper__value-input::-webkit-outer-spin-button,
.stepper__value-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.stepper__unit-label {
  font: var(--text-label-sm);
  font-size: 11px;
  color: var(--c-on-surface-variant);
  font-weight: 600;
  opacity: 0.8;
}
.stepper__star-icon {
  color: #f59e0b;
  filter: drop-shadow(0 1px 1px rgba(245, 158, 11, 0.25));
}

/* Tiered-discount strip under the quantity stepper: shows the volume
   discount the CURRENT quantity already earns and how far the next tier
   is (see paintTierProgress in select-service.js). */
.tier-strip {
  margin-top: 10px;
  padding: 10px 14px;
  border-radius: var(--radius-xl);
  background: color-mix(in srgb, var(--c-tertiary) 7%, rgba(255, 255, 255, 0.7));
  border: 1px solid color-mix(in srgb, var(--c-tertiary) 20%, transparent);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.tier-strip__dots {
  display: flex;
  gap: 6px;
}
.tier-strip__dot {
  flex: 1;
  height: 5px;
  border-radius: var(--radius-full);
  background: rgba(100, 116, 139, 0.18);
  transition: background-color 0.2s ease;
}
.tier-strip__dot--on {
  background: var(--c-tertiary-container);
}
.tier-strip__text {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.tier-strip__current {
  display: flex;
  align-items: center;
  gap: 5px;
  font: var(--text-label-md);
  font-weight: 800;
}
.tier-strip__current .icon {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}
.tier-strip__current--on {
  color: #15803d;
}
.tier-strip__current--none {
  color: var(--c-on-surface-variant);
  font-weight: 700;
}
.tier-strip__next {
  font: var(--text-label-sm);
  font-weight: 600;
  color: var(--c-on-surface-variant);
  line-height: 1.6;
}
.tier-strip__next b {
  color: var(--c-primary-container);
  font-weight: 800;
}
.tier-strip__next--max {
  color: #15803d;
  font-weight: 700;
}

/* Header row above the number box: field label + per-unit rate, used on
   the stars stepper card. */
.stepper-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
  padding-inline: 2px;
}
.stepper-card__label {
  font: var(--text-label-md);
  font-weight: 700;
  color: var(--c-on-surface-variant);
}
.stepper-card__rate {
  font: var(--text-label-sm);
  color: var(--c-on-surface-variant);
  font-weight: 600;
}

/* ---------------------------------------------------------------------
   Preset chips
--------------------------------------------------------------------- */
.chip-row {
  /* Flex-wrap instead of a fixed 3-column grid: with 5 presets the second
     row (2 chips) centers itself under the first instead of leaving an
     empty slot on one side. */
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
}
.chip-row > .chip {
  /* exactly 3 per row: (100% - 2 gaps) / 3 */
  flex: 0 0 calc((100% - 20px) / 3);
  min-width: 0;
  text-align: center;
}
.chip {
  padding: 10px 4px;
  border-radius: var(--radius-lg);
  font: var(--text-label-md);
  font-weight: 800;
  text-align: center;
  /* Frosted-glass surface for EVERY chip (selected or not). The old white
     border + white fill vanished against the white card behind it, so the
     unselected presets read as plain text. A translucent slate border and a
     faint cool tint keep them visibly tappable while staying light. */
  /* PERF: blur removed (6 chips = 6 blur layers); alpha raised to compensate. */
  background:
    linear-gradient(160deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.7) 100%),
    rgba(148, 163, 184, 0.1);
  border: 1px solid rgba(100, 116, 139, 0.24);
  color: var(--c-on-surface-variant);
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
  box-shadow: 0 2px 8px rgba(15, 23, 42, 0.05), inset 0 1px 0 rgba(255, 255, 255, 0.85);
}
.chip:active {
  transform: scale(0.97);
}
.chip--active {
  /* Solid near-white fill + a clear-but-thin accent border — NOT a
     tinted accent-colour gradient — so the selected chip stays easy to
     read even sitting on top of an already-tinted panel (e.g. the stars
     tab's own yellow wash): a soft coloured chip on a soft coloured
     background was low-contrast and busy. Colour comes from --accent
     (brand blue by default, overridden to each tab's own colour inside
     .premium-panel--stars/--gift below). Border is a single 1px line,
     no added box-shadow ring on top of it — that combination was what
     made the selected state look thick/heavy. */
  border-color: transparent;
  background: var(--accent-tint, rgba(0, 152, 234, 0.14));
  color: var(--accent-ink, var(--c-primary-container));
  font-weight: 800;
  transform: scale(1.03);
  box-shadow: 0 2px 10px rgba(15, 23, 42, 0.1);
}
.chip--stacked {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  /* Taller/roomier than before (was 88px) — part of giving the stars
     panel's content the same visual weight as premium/gift next to it. */
  padding: 16px 6px;
  min-height: 100px;
  height: auto;
}
/* Fixed-height slot at the top of every preset card. Holds either the green
   "٪N ارزون‌تر" pill or, on the baseline card, a neutral "قیمت پایه" pill —
   so all six cards have the same three rows (badge / number / tag) and
   line up exactly, instead of the pill-less cards looking shorter and
   off-balance. */
.chip__badge-slot {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 20px;
}
.chip__base {
  display: inline-flex;
  align-items: center;
  font: var(--text-label-sm);
  font-size: 10px;
  font-weight: 700;
  color: var(--c-on-surface-variant);
  background: rgba(100, 116, 139, 0.1);
  border: 1px solid rgba(100, 116, 139, 0.2);
  border-radius: var(--radius-full);
  padding: 1px 8px;
  line-height: 1.6;
  white-space: nowrap;
}
.chip--active .chip__base {
  color: var(--accent-ink, var(--c-primary-container));
  background: rgba(255, 255, 255, 0.55);
  border-color: transparent;
}
.chip__qty {
  font: var(--text-label-lg);
  font-weight: 800;
}
.chip__tag {
  font: var(--text-label-sm);
  font-weight: 700;
  color: var(--accent, var(--c-primary));
  line-height: 1.1;
}
.chip__tag--muted {
  color: var(--c-on-surface-variant);
  font-weight: 600;
}
/* Real, computed-from-price discount badge (see fetchPresetDiscounts in
   select-service.js) — a small green pill above the quantity so the "این
   بسته ارزون‌تره" signal reads before the number itself. */
.chip__discount {
  display: inline-flex;
  align-items: center;
  font: var(--text-label-sm);
  font-size: 10px;
  font-weight: 800;
  color: #15803d;
  background: rgba(34, 197, 94, 0.14);
  border: 1px solid rgba(34, 197, 94, 0.28);
  border-radius: var(--radius-full);
  padding: 1px 8px;
  line-height: 1.6;
  white-space: nowrap;
}
.chip--active .chip__discount {
  color: #166534;
  background: rgba(34, 197, 94, 0.22);
  border-color: rgba(34, 197, 94, 0.4);
}
.stars-pack-hint {
  font: var(--text-label-sm);
  color: var(--c-on-surface-variant);
  margin: 0 0 10px;
  line-height: 1.5;
}
.chip-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 14px 0;
  border-radius: var(--radius-xl);
  border: 1px solid color-mix(in srgb, var(--c-outline-variant) 80%, transparent);
  background: var(--c-surface-container-lowest);
  color: var(--c-secondary);
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.chip-tile--active {
  border-color: transparent;
  background: var(--c-primary);
  color: var(--c-on-primary);
}
.chip-tile__num {
  font: var(--text-headline-sm);
  font-weight: 700;
}
.chip-tile__unit {
  font: var(--text-label-sm);
}

/* ---------------------------------------------------------------------
   Text input
--------------------------------------------------------------------- */
.field-label {
  display: block;
  font: var(--text-label-lg);
  color: var(--c-on-surface);
  font-weight: 600;
  margin-bottom: var(--space-xs);
}
.input-wrap {
  position: relative;
  display: flex;
  align-items: center;
  margin-bottom: 8px;
}
.input-wrap__prefix {
  position: absolute;
  left: 12px;
  color: var(--c-secondary);
  font-weight: 700;
  font-size: 16px;
  pointer-events: none;
  user-select: none;
}
.text-input {
  width: 100%;
  /* Very light blue-grey plate + soft border: the field stays visible on the
     white card instead of blending into it (valid / invalid tint it below). */
  background: #f3f6fb;
  border: 1px solid #d3dce8;
  border-radius: var(--radius-xl);
  padding: 14px 12px 14px 36px;
  font: var(--text-body-md);
  color: var(--c-on-surface);
  font-weight: 600;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.03);
  transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}
/* Room on the right for the validation tick (the "@" prefix sits on the left). */
.input-wrap .text-input {
  padding-right: 42px;
}
/* dir="ltr" alone doesn't flip text-align — the page is RTL overall and
   text-align isn't tied to the dir attribute, so an LTR field (links,
   usernames, phone numbers) still rendered its text/placeholder flush
   right unless told otherwise. */
.text-input[dir="ltr"] {
  text-align: left;
}
.text-input::placeholder {
  color: var(--c-outline);
}
.text-input:focus {
  outline: none;
  background: var(--select-bg);
  box-shadow: none;
}
/* Live validation on the recipient field. --invalid = a definite format
   problem (soft red border); --valid = a small green tick in the corner. */
.input-wrap--invalid .text-input,
.input-wrap--invalid .text-input:focus {
  background: #fff5f5;
  border-color: rgba(239, 68, 68, 0.55);
  box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1), inset 0 1px 3px rgba(0, 0, 0, 0.03);
}
.input-wrap--valid .text-input,
.input-wrap--valid .text-input:focus {
  background: #f1fbf6;
  border-color: rgba(16, 185, 129, 0.45);
  box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.08), inset 0 1px 3px rgba(0, 0, 0, 0.03);
}
.input-wrap__check {
  position: absolute;
  right: 10px;
  width: 22px;
  height: 22px;
  border-radius: var(--radius-full);
  display: flex;
  align-items: center;
  justify-content: center;
  /* White tick on a solid green disc — the old dark-green tick on a 14%
     green tint barely read against the field. */
  color: #ffffff;
  background: var(--c-tertiary-container);
  box-shadow: 0 2px 6px rgba(16, 185, 129, 0.35);
  opacity: 0;
  transform: scale(0.6);
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.input-wrap__check .icon {
  width: 15px;
  height: 15px;
}
/* The path carries its own stroke attributes, so the override has to
   target the path itself (not the <svg>) to actually take effect. */
.input-wrap__check .icon path {
  stroke: #ffffff;
  stroke-width: 3;
}
.input-wrap--valid .input-wrap__check {
  opacity: 1;
  transform: scale(1);
}
@media (prefers-reduced-motion: reduce) {
  .input-wrap__check {
    transition: opacity 0.01s linear;
    transform: none;
  }
}
/* keep the status line off the recipient card above it */
.recipient-preview + .field-status {
  margin-top: 12px;
}
.field-status {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font: var(--text-label-sm);
  min-height: 16px;
}
.field-status__hint {
  color: var(--c-secondary);
}
.field-status__ok {
  display: flex;
  align-items: center;
  gap: 4px;
  color: var(--c-secondary);
}
.field-status__ok-badge {
  color: var(--c-tertiary);
  font-weight: 700;
}
.field-status__error {
  color: var(--c-error);
  font-weight: 600;
}

/* ---------------------------------------------------------------------
   Gift message (optional text box) —
   confirm-order screen, gift orders only.
--------------------------------------------------------------------- */
.text-input--textarea {
  padding: 12px 14px;
  resize: none;
  font-family: inherit;
  line-height: 1.6;
  min-height: 76px;
}
.checkbox-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 4px;
  font: var(--text-body-sm);
  color: var(--c-on-surface);
  cursor: pointer;
}
.checkbox-row input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin-top: 2px;
  accent-color: var(--c-primary);
  flex-shrink: 0;
}

/* Recipient name + avatar preview, shown under the username field once a
   Telegram lookup succeeds (see resolveRecipient in api.js). Hidden by
   default — most lookups miss (Bot API can only resolve users it has
   already seen), so this only appears on an actual match. */
.recipient-preview {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  margin-top: 8px;
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--c-tertiary) 8%, var(--c-surface-container-low));
  border: 1px solid color-mix(in srgb, var(--c-tertiary) 20%, transparent);
  animation: recipient-preview-in 0.18s ease;
}
.recipient-preview .recipient-card {
  width: 100%;
}
/* Same look as .recipient-preview (tinted plate + border), for the order
   summary's «تحویل‌گیرنده» row on the confirm screen. */
.recipient-box {
  display: flex;
  align-items: center;
  min-width: 0;
  padding: 8px 10px;
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--c-tertiary) 8%, var(--c-surface-container-low));
  border: 1px solid color-mix(in srgb, var(--c-tertiary) 20%, transparent);
}
@keyframes recipient-preview-in {
  from { opacity: 0; transform: translateY(-2px); }
  to { opacity: 1; transform: translateY(0); }
}
.recipient-preview__avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
  background: var(--c-surface-container-high);
}
.recipient-preview__avatar--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--c-outline);
}
.recipient-preview__avatar--placeholder .icon {
  width: 20px;
  height: 20px;
}
.recipient-preview__name {
  font: var(--text-body-sm);
  font-weight: 700;
  color: var(--c-on-surface);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.recipient-preview__check {
  color: var(--c-tertiary);
  flex-shrink: 0;
}
.recipient-preview__check .icon {
  width: 16px;
  height: 16px;
}

/* Two-line "who is this actually going to" card — full name on top,
   @username underneath, avatar photo beside both. Shared markup between
   screen 1 (public/js/lib/recipient-card.js) and screen 2's order
   summary, so the buyer sees the same visual confirmation in both places. */
/* Avatar on the LEFT, name with the @username stacked right under it, and
   the verified tick (when shown) on the RIGHT. The markup order is avatar,
   text, tick; row-reverse flips that for RTL so the avatar ends up leftmost. */
.recipient-card {
  display: flex;
  flex-direction: row-reverse;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.recipient-card__avatar {
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
  background: var(--c-surface-container-high);
}
.recipient-card__avatar--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--c-outline);
}
.recipient-card__avatar--placeholder .icon {
  width: 60%;
  height: 60%;
}
.recipient-card__lines {
  display: flex;
  flex-direction: column;
  /* both lines start on the same left edge, right beside the avatar (the
     @username is LTR text, which otherwise ends up offset from the name) */
  align-items: flex-end;
  min-width: 0;
  gap: 1px;
}
.recipient-card__fullname {
  font: var(--text-body-sm);
  font-weight: 700;
  color: var(--c-on-surface);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.recipient-card__username {
  font: var(--text-label-sm);
  color: var(--c-secondary);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.recipient-card__check {
  color: var(--c-tertiary);
  flex-shrink: 0;
  display: flex;
  /* pushes the tick to the far right of a full-width card */
  margin-left: auto;
}
.recipient-card__check .icon {
  width: 16px;
  height: 16px;
}

/* ---------------------------------------------------------------------
   Badges
--------------------------------------------------------------------- */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: var(--radius-full);
  font: var(--text-label-sm);
  font-weight: 700;
}
.badge--primary {
  color: var(--c-primary);
  background: color-mix(in srgb, var(--c-primary-fixed) 50%, transparent);
}
.badge--success {
  color: var(--c-tertiary);
  background: color-mix(in srgb, var(--c-tertiary) 10%, transparent);
}

/* ---------------------------------------------------------------------
   Info / guarantee micro-card
--------------------------------------------------------------------- */
.info-note {
  display: flex;
  align-items: center;
  gap: 10px;
  background: rgba(255, 255, 255, 0.85);
  border: 1px solid var(--glass-border);
  border-radius: 18px;
  padding: 12px 14px;
}
.info-note__icon {
  color: var(--c-primary);
  flex-shrink: 0;
  width: 20px;
  height: 20px;
}
.info-note__text {
  font: var(--text-label-sm);
  color: var(--c-on-surface-variant);
  line-height: 1.6;
}

/* Same shape as .info-note, amber tint — used for the pre-payment
   "turn off your VPN" heads-up in confirm-order.js. Not a popup on
   purpose: it sits in the normal scroll flow, right above the pay button,
   with no extra tap required to dismiss it. */
.info-note--warning {
  background: rgba(255, 251, 235, 0.85);
  border-color: rgba(217, 119, 6, 0.3);
}
.info-note--warning .info-note__icon {
  color: #d97706;
}

/* ---------------------------------------------------------------------
   Error banner
--------------------------------------------------------------------- */
.error-banner {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: var(--c-error-container);
  border: 1px solid color-mix(in srgb, var(--c-error) 30%, transparent);
  border-radius: var(--radius-lg);
  padding: 12px;
}
.error-banner__icon {
  color: var(--c-error);
  flex-shrink: 0;
  width: 20px;
  height: 20px;
}
.error-banner__text {
  font: var(--text-label-sm);
  color: var(--c-on-error-container);
  line-height: 1.6;
  flex: 1;
}

/* ---------------------------------------------------------------------
   Buttons
--------------------------------------------------------------------- */
.btn-primary {
  width: 100%;
  height: 54px;
  background: var(--c-primary-gradient);
  color: var(--c-on-primary);
  border: 1px solid rgba(56, 189, 248, 0.4);
  border-radius: var(--radius-xl);
  font: var(--text-label-lg);
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-shadow: var(--glow-sky);
  transition: transform 0.15s ease, filter 0.15s ease;
}
.btn-primary:active {
  transform: scale(0.98);
  filter: brightness(1.05);
}
/* Dimmed + flat instead of the loud gradient — otherwise a disabled
   ادامه (e.g. no premium plan picked yet) still looks fully clickable. */
.btn-primary:disabled {
  background: #cbd5e1;
  color: #64748b;
  border-color: transparent;
  box-shadow: none;
}
.btn-secondary {
  width: 100%;
  height: 48px;
  background: rgba(255, 255, 255, 0.6);
  color: var(--c-on-surface);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-xl);
  font: var(--text-label-lg);
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  transition: background-color 0.15s ease;
}
.btn-secondary:active {
  background: rgba(255, 255, 255, 0.85);
}
.btn-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

/* Loading state (see js/lib/button.js). The label + icon fade out but keep
   their space, so the button never changes width; a spinner fades in on top.
   Deliberately NOT :disabled — that would dim the whole button to 50%,
   which reads as "broken" rather than "working". pointer-events:none is what
   blocks repeat taps (JS also guards against them). */
.btn-primary,
.btn-secondary {
  position: relative;
}
.btn-primary > :not(.btn-spinner),
.btn-secondary > :not(.btn-spinner) {
  transition: opacity 0.18s ease;
}
.is-loading {
  pointer-events: none;
  cursor: progress;
}
.is-loading > :not(.btn-spinner) {
  opacity: 0;
}
.btn-spinner {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 22px;
  height: 22px;
  border-radius: var(--radius-full);
  border: 2.5px solid color-mix(in srgb, currentColor 28%, transparent);
  border-top-color: currentColor;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease;
  animation: btn-spin 0.7s linear infinite;
}
.is-loading > .btn-spinner {
  opacity: 1;
}
@keyframes btn-spin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  /* a spinner is functional feedback, so slow it down rather than remove it */
  .btn-spinner {
    animation-duration: 1.6s;
  }
}

/* ---------------------------------------------------------------------
   Bottom action dock
--------------------------------------------------------------------- */
.action-dock {
  /* Same recipe as the tab bar / price dock: a floating, solid white
     rounded card inset from the screen edges — not a full-width strip
     glued to the bottom, and no blur. */
  position: fixed;
  left: 12px;
  right: 12px;
  /* No tab bar sits under this dock (confirm / result screens), so it hugs
     the bottom edge instead of reusing the 28px tab-bar offset, which left
     a big empty gap under the pay buttons. */
  bottom: 8px;
  z-index: 40;
  max-width: calc(var(--app-max-width, 100vw) - 24px);
  margin: 0 auto;
  background: var(--glass-bar-bg);
  border: 1px solid var(--glass-bar-border);
  border-radius: var(--radius-2xl);
  box-shadow: 0 12px 36px rgba(15, 23, 42, 0.12);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: max(0px, env(safe-area-inset-bottom), var(--tg-safe-area-bottom));
}
.action-dock--split {
  flex-direction: row;
}
/* Gift / premium confirm screen: the dock holds two rows (back + pay, then
   pay-with-Stars), so it was ~14px taller than the one-row stars dock and
   its top edge sat much higher up the screen. Tighter padding + row gap and
   no extra margin on the second row keep it as short as possible. */
.action-dock--stacked {
  padding: 10px;
  gap: 6px;
}
.action-dock--split .btn-primary,
.action-dock--split .btn-secondary {
  flex: 1;
  width: auto;
}
/* Back is a secondary escape hatch on the confirm+pay screen — most people
   are here to finish, not to leave — so it shrinks to a small icon-only
   button (~20% of the dock) and lets "پرداخت" (~80%) read as the obvious
   next step. */
.action-dock--split .btn-secondary--compact {
  flex: 0 0 auto;
  width: 54px;
  padding: 0;
}

.dock-dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding-block: 2px;
}
.dock-dots__dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--c-surface-container-highest);
}
.dock-dots__dot--active {
  width: 10px;
  height: 10px;
  background: var(--c-primary);
}
.dock-dots__dot--done {
  background: var(--c-primary);
}

/* ---------------------------------------------------------------------
   Skeleton loading placeholder
--------------------------------------------------------------------- */
.skeleton {
  display: inline-block;
  border-radius: var(--radius-sm);
  background: linear-gradient(
    90deg,
    var(--c-surface-container-high) 25%,
    var(--c-surface-container-highest) 37%,
    var(--c-surface-container-high) 63%
  );
  background-size: 400% 100%;
  animation: skeleton-loading 1.4s ease infinite;
}
@keyframes skeleton-loading {
  0% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0 50%;
  }
}
@media (prefers-reduced-motion: reduce) {
  .skeleton {
    animation: none;
  }
}

/* ---------------------------------------------------------------------
   Bottom stack (select screen): the price dock and the tab bar live in ONE
   fixed container and stack with a fixed gap. They used to be two
   independently fixed elements whose offsets were guessed from a magic
   tab-bar height — and only the price dock added the Telegram safe-area
   inset, so on devices reporting one the two drifted ~35px apart.
--------------------------------------------------------------------- */
.bottom-stack {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: var(--tabbar-bottom);
  z-index: 45;
  max-width: calc(var(--app-max-width, 100vw) - 24px);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--dock-gap);
  /* the gap between the two bars stays click-through to the content behind */
  pointer-events: none;
}
.bottom-stack > * {
  pointer-events: auto;
}
/* White fade behind the floating bars, full viewport width, so scrolled
   content underneath softens to white before it reaches the tab bar /
   price dock instead of visually colliding with them. Click-through. */
.bottom-stack::before {
  content: "";
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  /* Tall enough to reach past the dock even on the premium tab, where the
     whole stack sits higher (see .bottom-stack:has(.price-dock--amount-hidden)) —
     otherwise the fade stopped short and the button's bottom edge showed
     a hard cutoff against the page instead of softening into it. */
  height: calc(var(--tabbar-bottom) + 120px);
  background: linear-gradient(to top, #ffffff 0%, rgba(255, 255, 255, 0.85) 40%, rgba(255, 255, 255, 0) 100%);
  z-index: -1;
  pointer-events: none;
}

/* ---------------------------------------------------------------------
   Service switcher — top-of-screen card picker (replaces the old bottom
   glass pill tab bar). Three equal cards in a row, each: colour-gradient
   icon tile on top, bold label, small grey subtitle underneath. The
   selected card gets a pale brand-tinted background + border; the other
   two stay plain/transparent. Scrolls away with the rest of the content
   (it's a normal flex child of .app-main, not fixed), so it sits right
   under the header instead of floating over the bottom of the screen.
--------------------------------------------------------------------- */

.service-switcher {
  display: grid;
  /* 4 tabs now (stars / gift / premium / reaction) — was repeat(3, 1fr)
     before the ری‌اکشن tab was added. */
  grid-template-columns: repeat(4, 1fr);
  /* One flat white bar, not three separate floating cards — the three
     items now share this single background/border, with their own
     dividers (below) marking the split instead of gaps + per-item
     borders. A real drop shadow (not just a border) so it reads as a
     raised surface against the page like every other card/panel here. */
  gap: 0;
  background: #fff;
  border: 1px solid var(--glass-border);
  border-radius: 18px;
  padding: 6px;
  box-shadow: var(--glass-shadow);
}
.service-switcher__item {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  padding: 10px 6px;
  border-radius: 14px;
  background: transparent;
  border: none;
  color: var(--c-on-surface);
  text-align: center;
  transition: background-color 0.15s ease, transform 0.15s ease;
}
/* Divider between items — short, vertically-centred hairline (NOT
   full-height) sitting in the gap BEFORE every item except the first,
   drawn with ::before instead of a border so its height is independent
   of the item's own box. It hides next to whichever item is active —
   either the item itself or the one right before it — so the selected
   pill's own edge reads cleanly instead of a line cutting across it.
   (Earlier version used `border-inline-end` with a
   "this item OR the next one" hide rule; with the DOM order actually
   being استارز/گیفت/پرمیوم, RTL display reverses it visually, so that
   rule hid the WRONG neighbour's line — selecting استارز silently
   erased the گیفت|پرمیوم divider too. Checking "this item OR the
   PREVIOUS one" via ::before fixes that regardless of DOM/visual order.) */
.service-switcher__item:not(:first-child)::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  /* Short hairline, not full-height — centred, roughly a third of the
     item's own height (matches the reference image's short divider). */
  top: 32%;
  bottom: 32%;
  width: 1px;
  background: rgba(15, 23, 42, 0.14);
}
.service-switcher__item.service-switcher__item--active::before,
.service-switcher__item--active + .service-switcher__item::before {
  background: transparent;
}
.service-switcher__item--active {
  /* One flat, even light-blue tint — no separate border colour, so the
     active pill reads as a single smooth fill, not an outlined card. */
  background: var(--select-bg);
}
.service-switcher__item:active {
  transform: scale(0.97);
}
/* Chained with .tile (not just its own class) so this --tile-size override
   reliably beats the base .tile rule's own default no matter which one
   happens to come later in the file — equal single-class specificity would
   otherwise leave it to source order, which is fragile. Same fix applied
   to .service-hero__badge and .plan-card__crown below. */
.tile.service-switcher__icon {
  --tile-size: 46px;
  --tile-glyph: 24px;
  margin-bottom: 2px;
}
.service-switcher__label {
  font: var(--text-label-sm);
  font-size: 14px;
  font-weight: 800;
  color: var(--c-on-surface);
}
.service-switcher__sub {
  font-size: 11px;
  line-height: 1.3;
  font-weight: 500;
  color: var(--c-on-surface-variant);
}
.service-switcher__item--active .service-switcher__label {
  color: var(--c-primary);
}



/* ---------------------------------------------------------------------
   Screen transition (subtle fade+rise on navigation)
--------------------------------------------------------------------- */
.screen-enter {
  animation: screen-enter-anim 0.22s ease;
}
@keyframes screen-enter-anim {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
@media (prefers-reduced-motion: reduce) {
  .screen-enter {
    animation: none;
  }
}

/* ---------------------------------------------------------------------
   Service hero (icon badge + title + description, top of select screen)
--------------------------------------------------------------------- */
.service-hero[hidden] {
  /* [hidden] alone loses to this file's own `.service-hero { display:flex }`
     below (same specificity, but this stylesheet is the later/author origin
     either way) — so premium's `el.hero.hidden = true` (select-service.js)
     needs this explicit override to actually disappear instead of leaving
     an empty flex item + gap above the unified premium-panel. */
  display: none;
}
.service-hero {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 14px;
  overflow: hidden;
  /* Was plain text sitting on the page background — now a real card like
     everything else on this screen (switcher, plan cards, gift cards), so
     it doesn't read as an unfinished/placeholder block. */
  padding: var(--space-md) var(--space-lg);
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.92) 0%, rgba(255, 255, 255, 0.72) 100%);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-2xl);
  box-shadow: var(--glass-shadow);
}
/* A soft, blurred colour blob behind the icon — one per service, same
   colour family as its tile gradient — for a bit of the glow/sparkle the
   plain white card was missing. Purely decorative, clipped by `overflow:
   hidden` above, and kept behind the real content via z-index. */
.service-hero::before {
  content: "";
  position: absolute;
  width: 150px;
  height: 150px;
  border-radius: 50%;
  top: -55px;
  inset-inline-end: -45px;
  filter: blur(8px);
  opacity: 0.55;
  pointer-events: none;
}
.service-hero > * {
  position: relative;
  z-index: 1;
}
.service-hero--stars::before {
  background: radial-gradient(circle, #fbbf24 0%, rgba(251, 191, 36, 0) 70%);
}
.service-hero--gift::before {
  background: radial-gradient(circle, #fb7185 0%, rgba(251, 113, 133, 0) 70%);
}
.service-hero--premium::before {
  background: radial-gradient(circle, #a78bfa 0%, rgba(167, 139, 250, 0) 70%);
}
.tile.service-hero__badge {
  /* Bigger than before (was sized like .tile--lg) so the hero reads as the
     page's headline visual, not just another small row icon. The colour/
     gloss still comes from .tile + its type modifier (select-service.js). */
  --tile-size: 64px;
  --tile-glyph: 32px;
}
.service-hero__title {
  font: var(--text-headline-md);
  color: var(--c-on-surface);
  font-weight: 900;
  letter-spacing: -0.01em;
  display: flex;
  align-items: center;
  gap: 6px;
}
.service-hero__title-icon {
  width: 16px;
  height: 16px;
  color: #fbbf24;
}
.service-hero__desc {
  font: var(--text-body-sm);
  color: var(--c-on-surface-variant);
  line-height: 1.7;
  margin-top: 2px;
}

/* ---------------------------------------------------------------------
   Gift grid + gift card
--------------------------------------------------------------------- */
.gift-grid {
  display: grid;
  /* 3 per row: the cards were far bigger than a gift needs to be. */
  grid-template-columns: repeat(3, 1fr);
  gap: 10px 8px;
}
/* One muted line above the grid instead of the same tagline repeated inside
   every card. */
.gift-hint {
  margin: 0 0 var(--space-sm);
  text-align: center;
  font: var(--text-label-sm);
  color: var(--c-on-surface-variant);
}
.gift-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  /* compact card: emoji on top, name, then the star price pill under it
     (no longer a corner badge — at 3 per row there is no room for that) */
  padding: 14px 4px 10px;
  border-radius: 18px;
  border: 1px solid var(--glass-border);
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.82) 0%, rgba(255, 255, 255, 0.52) 100%);
  box-shadow: var(--glass-shadow);
  text-align: center;
  transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
/* The odd-one-out last card (see giftCardHtml's fullWidth arg): spans
   both grid columns instead of sitting alone at half width, and switches
   to a horizontal layout (emoji beside the text) so that extra width is
   actually used instead of just stretching a vertical card sideways. */
.gift-card--full {
  grid-column: 1 / -1;
  flex-direction: row;
  /* Emoji and text as two balanced ends of the row, not left-packed —
     with only two items in a wide card, flex-start left most of the
     card's width empty on one side. DOM order is emoji-wrap then text,
     so plain `row` in this RTL layout already puts the emoji on the
     right and the text on the left, matching the rest of the app. */
  justify-content: space-between;
  align-items: center;
  text-align: right;
  gap: 16px;
  /* Roomier than the vertical cards' top padding since the price badge no
     longer floats above the content here — see .gift-card--full
     .gift-card__price below. */
  padding: var(--space-lg);
}
.gift-card--full .gift-card__name {
  margin-top: 0;
  font-size: 15px;
}
/* In the horizontal layout the price badge no longer has a reserved strip
   above the emoji to float into, so it drops out of absolute positioning
   and sits inline under the name instead (tag, if any, stays in its
   corner — there's enough height for it not to collide). */
.gift-card--full .gift-card__price {
  position: static;
  margin-top: 2px;
}
.gift-card--full .gift-card__emoji-wrap {
  flex-shrink: 0;
}
/* Noticeably bigger than the grid cards' emoji — this is the one "hero"
   card on the tab, it should look like a deliberate highlight, not a
   stretched-out regular card. */
.gift-card--full .gift-card__emoji {
  width: 84px;
  height: 84px;
  font-size: 64px;
}
.gift-card--full .gift-card__text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}
.gift-card--active {
  /* Selection colour now follows --accent/--accent-tint (brand blue by
     default, overridden to the gift tab's own pink/red inside
     .premium-panel--gift below) instead of the fixed --select-* blue, so
     it never clashes with the coloured panel it sits inside. A single 1px
     border, near-white fill, no extra box-shadow ring stacked on top —
     the earlier 1.5px border + 1px shadow ring together read as a thick
     double outline. */
  border-color: transparent;
  background: var(--accent-tint, rgba(0, 152, 234, 0.14));
  box-shadow: var(--glass-shadow-float);
  transform: scale(1.03);
}
/* The gift sits directly on the white card — no circle / tinted plate behind it. */
.gift-card__emoji {
  width: 46px;
  height: 46px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 36px;
  line-height: 1;
}
.gift-card__name {
  font: var(--text-label-md);
  font-size: 12px;
  font-weight: 700;
  color: var(--c-on-surface);
}
/* "+N ⭐ هدیه" under the gift name — see paintGiftRewards in select-service.js.
   Only shown for gifts whose star count clears the reward floor, so most
   small gift cards simply have nothing here (slot stays hidden). */
.gift-card__reward-slot {
  display: block;
  margin-top: 2px;
  font: var(--text-label-sm);
  font-size: 10px;
  font-weight: 800;
  color: var(--brand-deep);
}
.gift-card__reward-slot[hidden] {
  display: none;
}
/* Star price: small gold pill under the name. */
.gift-card__price {
  order: 3;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-top: 2px;
  padding: 2px 8px 2px 6px;
  border-radius: var(--radius-full);
  font: var(--text-label-md);
  font-size: 12px;
  font-weight: 800;
  line-height: 16px;
  color: #92400e;
  background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
  border: 1px solid rgba(217, 119, 6, 0.3);
  box-shadow: 0 2px 6px rgba(245, 158, 11, 0.2);
}
.gift-card__price-icon {
  width: 12px;
  height: 12px;
  color: #f59e0b;
  flex-shrink: 0;
}
/* "Limited" tag: top-right corner, opposite the price, so it never changes
   a card's height (all cards in a row stay the same). */
.gift-card__tag {
  position: absolute;
  top: 5px;
  right: 5px;
  font: var(--text-label-sm);
  font-size: 9px;
  font-weight: 700;
  line-height: 14px;
  padding: 1px 6px;
  border-radius: var(--radius-full);
  background: color-mix(in srgb, #e11d48 10%, transparent);
  color: #be123c;
}
.gift-card__emoji-wrap {
  position: relative;
  display: flex;
}
/* Selected-gift tick, pinned to the corner of the emoji. */
.gift-card__check svg {
  width: 11px;
  height: 11px;
}
.gift-card__check {
  position: absolute;
  right: -4px;
  bottom: -2px;
  width: 18px;
  height: 18px;
  border: 2px solid #fff;
  border-radius: var(--radius-full);
  background: var(--accent, var(--c-primary));
  color: var(--c-on-primary);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ---------------------------------------------------------------------
   Premium plan list
--------------------------------------------------------------------- */
/* ---------------------------------------------------------------------
   Premium: one unified panel — header (crown + title + desc, previously
   the separate #hero card) and the plan rows now share a single card
   shape/border/shadow instead of sitting in two disconnected cards.
--------------------------------------------------------------------- */
.premium-panel {
  position: relative;
  overflow: hidden;
  padding: 0;
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-2xl);
  box-shadow: var(--glass-shadow);
}
/* Same soft "liquid glass" colour blooms as the page background itself
   (#app::before/::after in base.css) — closest-side radial gradients,
   no blur() pass — but scaled to the card and spread across its FULL
   height (header + plan rows), not just a flat tint on the header strip.
   .premium-panel__header / .plan-list sit at z-index:1 above these. */
.premium-panel::before,
.premium-panel::after {
  content: "";
  position: absolute;
  z-index: 0;
  border-radius: 50%;
  pointer-events: none;
}
.premium-panel::before {
  top: -70px;
  inset-inline-end: -60px;
  width: 220px;
  height: 220px;
  background: radial-gradient(circle closest-side, rgba(167, 139, 250, 0.4) 0%, rgba(167, 139, 250, 0.16) 55%, rgba(167, 139, 250, 0) 100%);
}
.premium-panel::after {
  bottom: -60px;
  inset-inline-start: -70px;
  width: 240px;
  height: 240px;
  background: radial-gradient(circle closest-side, rgba(125, 211, 252, 0.35) 0%, rgba(125, 211, 252, 0.14) 55%, rgba(125, 211, 252, 0) 100%);
}
/* Stars tab — same panel shape, warm yellow/gold blooms instead of the
   premium purple/blue pair, so the tab reads in its own tile colour.
   --accent/--accent-tint/--accent-tint-strong/--accent-ink cascade down
   to every "selected" control inside this panel (preset chips, etc.) so
   selection highlights this tab's own gold instead of the generic blue. */
.premium-panel--stars::before {
  background: radial-gradient(circle closest-side, rgba(251, 191, 36, 0.4) 0%, rgba(251, 191, 36, 0.16) 55%, rgba(251, 191, 36, 0) 100%);
}
.premium-panel--stars::after {
  background: radial-gradient(circle closest-side, rgba(245, 158, 11, 0.3) 0%, rgba(245, 158, 11, 0.12) 55%, rgba(245, 158, 11, 0) 100%);
}
.premium-panel--stars {
  --accent: #d97706;
  --accent-ink: #92400e;
  --accent-tint: rgba(217, 119, 6, 0.12);
  --accent-tint-strong: rgba(217, 119, 6, 0.22);
}
/* Gift tab — pink/red blooms to match its tile colour. */
.premium-panel--gift::before {
  background: radial-gradient(circle closest-side, rgba(244, 63, 94, 0.35) 0%, rgba(244, 63, 94, 0.14) 55%, rgba(244, 63, 94, 0) 100%);
}
.premium-panel--gift::after {
  background: radial-gradient(circle closest-side, rgba(251, 113, 133, 0.3) 0%, rgba(251, 113, 133, 0.12) 55%, rgba(251, 113, 133, 0) 100%);
}
.premium-panel--gift {
  --accent: #e11d48;
  --accent-ink: #be123c;
  --accent-tint: rgba(225, 29, 72, 0.1);
  --accent-tint-strong: rgba(225, 29, 72, 0.2);
}
.premium-panel__header {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 22px var(--space-lg) var(--space-md);
  border-bottom: 1px solid rgba(15, 23, 42, 0.06);
}
.premium-panel__crown-img {
  flex-shrink: 0;
  width: 104px;
  height: auto;
  display: block;
}
/* Small crown chip beside the title — without it the title text floated
   alone on the left with nothing to balance it against the big crown
   picture on the right, which read as empty/unbalanced. */
.premium-panel__heading {
  display: flex;
  align-items: center;
  gap: 10px;
}
.premium-panel__title-icon {
  --tile-size: 34px;
  --tile-glyph: 18px;
  flex-shrink: 0;
}
.premium-panel__title {
  font: var(--text-headline-md);
  color: var(--c-on-surface);
  font-weight: 900;
  letter-spacing: -0.01em;
}
.premium-panel__desc {
  font: var(--text-body-sm);
  color: var(--c-on-surface-variant);
  line-height: 1.7;
  margin-top: 2px;
}
.plan-list {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  /* More breathing room between the three plan rows — they read as one
     dense block at 14px; this lets each card stand on its own. */
  gap: 12px;
  padding: var(--space-lg) var(--space-lg) var(--space-lg);
}
/* Generic "rest of the panel" slot — used by the stars/gift tabs to hold
   whatever sits below the shared header (stepper+presets, gift grid)
   inside the SAME .premium-panel card, instead of a separate card below
   it. Sits above the panel's colour blooms, same as .plan-list. */
.premium-panel__body {
  position: relative;
  z-index: 1;
  padding: var(--space-md) var(--space-lg) var(--space-lg);
}
/* The three feature lines are identical on every plan (marketing
   reassurance, not a per-plan diff), so they're shown ONCE here — as a
   compact wrapping row under the header — instead of being repeated
   inside each of the three cards below, which was most of the clutter. */
.premium-panel__features {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  list-style: none;
  padding: var(--space-md) var(--space-lg) var(--space-md);
  margin: 0 var(--space-lg);
  /* A clear divider from the plan rows below — otherwise the checklist and
     the first plan card (۳ ماهه) sit right on top of each other. */
  border-bottom: 1px solid rgba(15, 23, 42, 0.06);
}
.premium-panel__features li {
  display: flex;
  align-items: center;
  gap: 5px;
  font: var(--text-label-sm);
  font-size: 12px;
  font-weight: 600;
  color: var(--c-on-surface-variant);
  white-space: nowrap;
}
.premium-panel__feature-icon {
  width: 13px;
  height: 13px;
  color: #16a34a;
  flex-shrink: 0;
}
/* Reaction tab's "تعداد دلخواه" box — sits above the package grid as its
   own full-width row (not one grid cell among six) since it's the primary
   way to enter a count, not a seventh package option. */
.reaction-custom-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px var(--space-lg);
  border-radius: var(--radius-2xl);
  border: 1px solid rgba(255, 255, 255, 0.55);
  background: rgba(255, 255, 255, 0.78);
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06);
  transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
}
.reaction-custom-row--active {
  border-color: transparent;
  background: var(--select-bg);
  box-shadow: var(--glass-shadow-float);
}
.reaction-custom-row__icon {
  --tile-size: 36px;
  --tile-glyph: 18px;
  flex-shrink: 0;
}
.reaction-custom-row input#reaction-custom-input {
  width: 90px;
  text-align: center;
  flex-shrink: 0;
}
.reaction-custom-row__hint {
  font: var(--text-label-sm);
  font-size: 12.5px;
  color: var(--c-on-surface-variant);
}
/* Reaction tab's count box: the same −/+ stepper the stars tab uses, centred,
   with an always-editable number in the middle. */
.reaction-stepper__label {
  display: block;
  text-align: center;
}
.reaction-stepper {
  max-width: 340px;
  margin: 0 auto;
}
.reaction-stepper.reaction-custom-row--active {
  border-color: transparent;
  background: var(--select-bg);
  box-shadow: var(--glass-shadow-float);
}
.reaction-stepper .stepper__value-input {
  width: 5ch;
  min-width: 4ch;
}
.reaction-stepper__heart {
  color: #ef4444;
  filter: drop-shadow(0 1px 1px rgba(239, 68, 68, 0.25));
}
/* Compact 2-row x 3-col grid variant — used by the reaction tab's fixed
   packages instead of the tall one-per-row .plan-list, so six packages fit
   on screen without scrolling. Falls back to a single column on very
   narrow viewports so a card never gets squeezed unreadably thin. More
   breathing room than the first pass (10px) — at 10px with a visible card
   background the grid read as cramped/busy; wider gaps let each card's
   own edge actually register. */
.plan-list--grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px 8px;
  padding: var(--space-md) 4px var(--space-lg);
}
@media (max-width: 360px) {
  .plan-list--grid {
    grid-template-columns: repeat(2, 1fr);
  }
}
/* Grid cards: icon beside the count (one compact row) instead of stacked
   above it — stacking made each card taller than it needed to be and let
   the oversized icon dominate a small card. A flatter, near-white fill
   (instead of the full-width card's translucent white-on-white, which all
   but disappeared against the panel background at this size) gives each
   card an actual visible edge, and the smaller radius matches its smaller
   footprint instead of looking like a shrunk-down big card. */
.plan-card--grid {
  flex-direction: row;
  justify-content: flex-start;
  align-items: center;
  gap: 8px;
  padding: 12px 10px;
  text-align: start;
  border-radius: 14px;
  background: #f7f8fb;
  border-color: rgba(15, 23, 42, 0.06);
  box-shadow: none;
}
.plan-card--grid.plan-card--active {
  background: rgba(0, 152, 234, 0.14);
  border-color: transparent;
}
.plan-card--grid .plan-card__id {
  flex-direction: row;
  min-width: 0;
  gap: 8px;
}
.plan-card--grid .tile.plan-card__crown {
  --tile-size: 26px;
  --tile-glyph: 13px;
  flex-shrink: 0;
}
.plan-card--grid .plan-card__id-text {
  gap: 2px;
  min-width: 0;
}
.plan-card--grid .plan-card__months {
  font-size: 12.5px;
  white-space: nowrap;
}
.plan-card--grid .chip__tag {
  font-size: 10.5px;
}
/* Each plan card now only carries what actually differs between plans —
   duration (+ tag, on the one featured plan only) on the right, price on
   the left — one row, two balanced sides, nothing left empty. */
.plan-card {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  text-align: start;
  padding: 18px var(--space-lg);
  border-radius: var(--radius-2xl);
  border: 1px solid rgba(255, 255, 255, 0.55);
  background: rgba(255, 255, 255, 0.78);
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06);
  transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.plan-card--featured {
  /* Just a hint of its own colour (gold, matching its crown/badge) when not
     selected — the "بهترین انتخاب" badge already carries the "featured"
     message, so this stays subtle instead of a scaled-up card + ribbon.
     Same size as the other two cards (see .plan-card) — "featured" is
     carried by colour + badge only, never by growing the card. Extra top
     padding leaves clean room for .plan-card__badge pinned above it. */
  border-color: rgba(217, 119, 6, 0.28);
  background: rgba(254, 252, 232, 0.65);
  padding-top: 24px;
}
.plan-card--active {
  /* Same selection colour as the service switcher / stars presets / gift
     cards (--select-border), not its own darker blue — one consistent
     "this is picked" look across the whole purchase flow. Single 1px
     border, no stacked box-shadow ring — matches the lighter selected
     look used everywhere else now. */
  border-color: transparent;
  background: rgba(0, 152, 234, 0.14);
  box-shadow: var(--glass-shadow-float);
}
/* The one featured plan is already gold (border + tint + badge, see
   .plan-card--featured above) — switching it to the generic blue
   selection colour on tap clashed with its own gold identity right next
   to it (badge still gold, card suddenly blue). Selecting it instead
   deepens its OWN gold instead of overriding it with blue, so the card
   stays visually consistent whether picked or not. */
.plan-card--featured.plan-card--active {
  border: 1px solid rgba(217, 119, 6, 0.55);
  background: rgba(254, 243, 199, 0.8);
  box-shadow: var(--glass-shadow-float);
}
/* Right side: crown on top, duration below it, tag (only on the one
   featured plan) below that — stacked, not inline, so this side carries
   roughly the same visual weight/height as the 3-line price side on the
   left instead of reading as a thin, mostly-empty label next to it. */
.plan-card__id {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* More air between the crown icon and the "N ماهه" label below it —
     8px read as glued together. */
  gap: 8px;
  min-width: 56px;
  flex-shrink: 0;
}
.tile.plan-card__crown {
  --tile-size: 40px;
  --tile-glyph: 20px;
  flex-shrink: 0;
}
.plan-card__id-text {
  display: flex;
  flex-direction: column;
  gap: 5px;
  align-items: center;
}
.plan-card__months {
  font: var(--text-label-md);
  font-size: 14.5px;
  font-weight: 800;
  color: var(--c-on-surface);
  white-space: nowrap;
}
.plan-card__tag {
  font-size: 10.5px;
  font-weight: 800;
  padding: 2px 9px;
  border-radius: var(--radius-full);
  white-space: nowrap;
}
.plan-card__tag--sky {
  background: rgba(14, 165, 233, 0.12);
  color: #0284c7;
}
.plan-card__tag--premium {
  background: rgba(139, 92, 246, 0.14);
  color: #7c3aed;
}
.plan-card__tag--gift {
  background: rgba(244, 63, 94, 0.12);
  color: #e11d48;
}
.plan-card__tag--gold {
  background: rgba(217, 119, 6, 0.14);
  color: #b45309;
}
/* "بهترین انتخاب" banner — pinned to the top-center edge of the one
   featured plan card, overlapping its border slightly (like a ribbon),
   instead of sitting inline next to the months label down in the body.
   .plan-card gets extra top padding (see .plan-card--featured) to leave
   room for this so it never overlaps the crown/months row underneath. */
.plan-card__badge {
  position: absolute;
  top: -11px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 11px;
  font-weight: 800;
  padding: 3px 14px;
  border-radius: var(--radius-full);
  white-space: nowrap;
  box-shadow: 0 4px 10px -3px rgba(15, 23, 42, 0.25);
}
.plan-card__badge--gold {
  background: linear-gradient(135deg, #fde68a 0%, #f59e0b 100%);
  color: #78350f;
}
/* Left side: the price is the one number that should dominate, so it
   stays large; the fine print (monthly-equivalent + reward) sits under it. */
.plan-card__price-col {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  /* Price / monthly-equivalent / reward were stacked at 3px — almost
     touching. More room between the three lines so each is easy to pick
     out at a glance instead of reading as one dense block of numbers. */
  gap: 6px;
  flex-shrink: 0;
}
.plan-card__price {
  font: var(--text-headline-sm, var(--text-label-lg));
  font-size: 19px;
  font-weight: 900;
  color: var(--c-on-surface);
  white-space: nowrap;
}
.plan-card__monthly {
  font: var(--text-label-sm);
  font-size: 12px;
  font-weight: 600;
  color: #64748b;
  white-space: nowrap;
}
/* "+N ⭐ هدیه" under a premium plan's price — see setPremiumPlanPrice in select-service.js. */
.plan-card__reward {
  font: var(--text-label-sm);
  font-size: 11px;
  font-weight: 800;
  color: var(--brand-deep);
  white-space: nowrap;
}

/* ---------------------------------------------------------------------
   Sticky price + CTA footer (used by select screen, sits above tab bar)
--------------------------------------------------------------------- */
.price-dock {
  background: var(--glass-bar-bg);
  border: 1px solid var(--glass-bar-border);
  border-radius: 16px;
  /* This is the primary action on the screen (price + pay), so it needs to
     sit clearly above the feature-article guide card in visual weight —
     a stronger lift than the old, barely-there shadow. */
  box-shadow: 0 14px 32px -10px rgba(15, 23, 42, 0.18), 0 1px 0 rgba(255, 255, 255, 0.8) inset;
  padding: 10px 14px;
  /* Price on one side, call-to-action on the other (RTL: price on the right). */
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
}
.price-dock__info {
  flex: 0 0 auto;
  min-width: 0;
  text-align: start;
}
.price-dock__label {
  display: block;
  font: var(--text-label-sm);
  font-size: 12px;
  color: var(--c-on-surface-variant);
  font-weight: 500;
  margin-bottom: 2px;
}
.price-dock__value {
  font: 800 17px/22px var(--font-family);
  color: var(--c-on-surface);
  white-space: nowrap;
  min-height: 22px;
}
/* "+N ⭐ هدیه" under the dock price — see renderDockReward in select-service.js. */
.price-dock__reward {
  font: var(--text-label-sm);
  font-size: 11px;
  font-weight: 800;
  color: var(--brand-deep);
  white-space: nowrap;
  margin-top: 2px;
}
.price-dock .btn-primary {
  width: auto;
  flex: 1 1 0;
  min-width: 0;
  max-width: 220px;
  height: 46px;
  padding-inline: 14px;
  white-space: nowrap;
}
/* Stars tab only (see renderPriceDockVisibility): drops just the
   "مبلغ قابل پرداخت" caption, not the whole .price-dock__info block —
   unlike premium (below) the price number itself still needs to show
   here, so the dock reads as exactly two stacked lines: the price, then
   the "+N ⭐ هدیه" reward line right under it. */
.price-dock--label-hidden .price-dock__label {
  display: none;
}
/* Premium only (see renderPriceDockVisibility): the amount is already
   visible on the selected plan card above, so it's dropped from the dock
   here and the button alone stretches to fill the bar. With no price
   label left beside it, the white card-with-padding around a single
   button read as a redundant box-in-a-box — so here the dock sheds its
   own card chrome (background/border/shadow/padding) entirely and just
   lets the button itself float above the tab bar with breathing room. */
.price-dock--amount-hidden {
  background: none;
  border: none;
  box-shadow: none;
  padding: 0;
}
.price-dock--amount-hidden .price-dock__info {
  display: none;
}
.price-dock--amount-hidden .btn-primary {
  flex: 1 1 auto;
  max-width: none;
  /* Narrower than full-bleed (extra side margin). Lifting it off the
     bottom edge is done on .bottom-stack itself (see
     .bottom-stack:has(.price-dock--amount-hidden) below) rather than with
     margin here, so the white fade behind it (.bottom-stack::before)
     moves up together with it instead of leaving a hard edge underneath. */
  margin: 0 10px;
}
/* Premium tab specifically: push the whole floating stack (and its white
   fade) further up off the bottom edge than the default stars/gift dock.
   Keyed off a class on .bottom-stack itself (set alongside
   price-dock--amount-hidden in renderPriceDockVisibility) rather than
   :has(), which isn't reliably supported in older in-app WebViews. */
.bottom-stack--lifted {
  bottom: calc(var(--tabbar-bottom) + 14px);
}

/* ---------------------------------------------------------------------
   Icon helper (inline SVG sizing)
--------------------------------------------------------------------- */
.icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  fill: currentColor;
}

/* ---------------------------------------------------------------------
   Feature guide article (under the info note on the پرمیوم / استارز / گیفت tabs).
   Title + first few lines are always visible; a toggle button reveals the
   rest. js/lib/feature-article.js drives the max-height transition; the
   em-based value below is the no-JS-measurement fallback for the same
   4-line height (4 lines x 1.95 line-height = 7.8em).
--------------------------------------------------------------------- */
.feature-article {
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* header is no longer fixed over the scroller, so only a small breathing gap is needed */
  scroll-margin-top: var(--space-md);
  /* This is a secondary "read more" block, not an action — it shouldn't
     carry the same card weight as the price-dock / continue button below
     it (same white bg + shadow on both made them look like two competing
     CTAs). Flatter background, no float-shadow, thinner border. */
  background: rgba(255, 255, 255, 0.5);
  box-shadow: none;
  border: 1px solid rgba(15, 23, 42, 0.05);
}
.feature-article__head {
  display: flex;
  align-items: center;
  gap: 12px;
}
.feature-article__badge {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-xl);
  font-size: 24px;
  background: var(--fa-badge-bg, linear-gradient(135deg, #a5b4fc 0%, #818cf8 45%, #f0abfc 100%));
  box-shadow:
    0 8px 20px -6px var(--fa-badge-shadow, rgba(99, 102, 241, 0.5)),
    0 1px 0 rgba(255, 255, 255, 0.6) inset;
}
.feature-article__head-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.feature-article__kicker {
  font: var(--text-label-sm);
  font-weight: 800;
  color: #6366f1;
}
.feature-article__title {
  font: var(--text-headline-sm);
  font-weight: 900;
  line-height: 1.5;
  color: var(--c-on-surface);
}
.feature-article__body {
  position: relative;
  overflow: hidden;
  font-size: 13.5px;
  line-height: 1.95;
  color: #475569;
  transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.feature-article__body.is-collapsed {
  max-height: 7.8em;
  /* fade the last visible line out instead of chopping it mid-glyph */
  -webkit-mask-image: linear-gradient(to bottom, #000 60%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 60%, transparent 100%);
}
.feature-article__more {
  margin-top: 14px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.pa-section {
  --pa-tint: var(--c-primary);
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 18px;
  border-top: 1px dashed color-mix(in srgb, var(--c-outline) 50%, transparent);
}
.pa-section__head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.pa-section__sticker {
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-lg);
  font-size: 21px;
  line-height: 1;
  background: color-mix(in srgb, var(--pa-tint) 14%, #ffffff);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--pa-tint) 24%, transparent) inset;
}
.pa-section__title {
  font: var(--text-label-lg);
  font-weight: 800;
  line-height: 1.6;
  color: var(--c-on-surface);
}
.pa-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.pa-list__item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 9px 12px;
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.62);
  border: 1px solid rgba(255, 255, 255, 0.9);
  /* a coloured edge on the start side, in the section's tint */
  border-inline-start: 3px solid color-mix(in srgb, var(--pa-tint) 55%, transparent);
}
.pa-list__emoji {
  flex-shrink: 0;
  width: 24px;
  text-align: center;
  font-size: 18px;
  line-height: 1.6;
}
.pa-list__text {
  min-width: 0;
  font-size: 13px;
  line-height: 1.85;
}
.pa-list__text strong {
  font-weight: 800;
  color: var(--c-on-surface);
}
.pa-note {
  padding: 10px 12px;
  border-radius: var(--radius-lg);
  font-size: 12.5px;
  line-height: 1.85;
  color: #78350f;
  background: color-mix(in srgb, #f59e0b 10%, #ffffff);
  border: 1px solid color-mix(in srgb, #f59e0b 28%, transparent);
}
.feature-article__toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  height: 46px;
  border-radius: var(--radius-xl);
  font: var(--text-label-lg);
  color: var(--c-primary-container);
  background: color-mix(in srgb, var(--c-primary) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--c-primary) 22%, transparent);
  transition: background-color 0.15s ease, transform 0.1s ease;
}
.feature-article__toggle:active {
  transform: scale(0.98);
  background: color-mix(in srgb, var(--c-primary) 18%, transparent);
}
.feature-article__toggle:focus-visible {
  outline: 2px solid var(--c-primary);
  outline-offset: 2px;
}
.feature-article__toggle-icon {
  width: 20px;
  height: 20px;
  transition: transform 0.3s ease;
}
.feature-article__toggle[aria-expanded="true"] .feature-article__toggle-icon {
  transform: rotate(180deg);
}
@media (prefers-reduced-motion: reduce) {
  .feature-article__body,
  .feature-article__toggle-icon {
    transition: none;
  }
}

/* "برای خودم می‌خوام" — under the recipient field; fills in the buyer's own
   @username. Pressed/active while the field holds that username. */
.self-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: var(--space-sm);
  padding: 8px 14px 8px 12px;
  border-radius: var(--radius-full);
  font: var(--text-label-md);
  font-weight: 700;
  color: var(--c-primary-container);
  background: color-mix(in srgb, var(--c-primary) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--c-primary) 24%, transparent);
  transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
.self-btn .icon {
  width: 18px;
  height: 18px;
}
.self-btn:active {
  transform: scale(0.97);
}
.self-btn:focus-visible {
  outline: 2px solid var(--c-primary);
  outline-offset: 2px;
}
.self-btn--active {
  color: #ffffff;
  background: var(--c-primary);
  border-color: transparent;
}


/* ---------------------------------------------------------------------
   Header account menu (avatar + chevron + dropdown)
--------------------------------------------------------------------- */
.app-bar__account {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}
.acct {
  position: relative;
}
.acct__btn {
  /* Touch target sized to the (now 46px) avatar inside it. */
  min-width: 46px;
  min-height: 46px;
  padding: 0 2px;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 4px;
  /* The page is RTL, which would flip the [avatar][chevron] pair. Pin the
     pair's own order so the avatar always sits on the outer screen edge and
     the chevron tucks in beside it, right next to the avatar. */
  direction: ltr;
  border-radius: var(--radius-full);
  color: var(--c-on-surface-variant);
}
.acct__btn:active {
  opacity: 0.75;
}
.acct__btn:focus-visible,
.acct__item:focus-visible,
.terms-link:focus-visible,
.sheet__close:focus-visible,
.order-row:focus-visible {
  outline: 2px solid var(--brand-azure);
  outline-offset: 2px;
}
.acct__chev {
  display: flex;
  transition: transform 0.18s ease;
}
.acct__chev .icon {
  width: 16px;
  height: 16px;
}
.acct__btn[aria-expanded="true"] .acct__chev {
  transform: rotate(180deg);
}

.acct-avatar {
  position: relative;
  /* Was 36px — bumped up to match the bigger logo so the header's two
     ends feel balanced instead of the avatar reading small/secondary. */
  width: 46px;
  height: 46px;
  border-radius: var(--radius-full);
  flex-shrink: 0;
  background: var(--c-primary-gradient);
  box-shadow: 0 2px 8px -2px rgba(2, 132, 199, 0.45);
  border: 2px solid rgba(255, 255, 255, 0.95);
  display: block;
  /* the photo is clipped to the circle; the status dot sits OUTSIDE the clip */
}
.acct-avatar__fallback {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font: 800 18px/1 var(--font-family);
  border-radius: 50%;
}
.acct-avatar__img[hidden] {
  display: none;
}
.acct-avatar__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  opacity: 0;
  transition: opacity 0.18s ease;
}
.acct-avatar[data-has-photo="1"] .acct-avatar__img {
  opacity: 1;
}
.acct-avatar[data-has-photo="1"] .acct-avatar__fallback {
  opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
  .acct__chev { transition: none; }
}

.acct__menu {
  position: absolute;
  top: calc(100% + 6px);
  inset-inline-end: 0;
  z-index: 60;
  min-width: 190px;
  padding: 6px;
  border-radius: var(--radius-xl);
  /* Deliberately a plain, opaque white card — no blur/transparency — so the
     items stay easy to read over whatever is behind it. */
  background: #ffffff;
  border: 1px solid #e2e8f0;
  box-shadow: 0 10px 28px rgba(15, 23, 42, 0.16);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.acct__item {
  min-height: 44px;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 12px;
  border-radius: var(--radius-lg);
  font: var(--text-label-lg);
  font-weight: 600;
  color: var(--c-on-surface);
  text-align: start;
}
.acct__item .icon {
  width: 20px;
  height: 20px;
  color: var(--brand-blue);
  flex-shrink: 0;
}
.acct__item:active,
.acct__item:hover {
  background: rgba(2, 132, 199, 0.08);
}

/* ---------------------------------------------------------------------
   Bottom sheet (terms, orders)
--------------------------------------------------------------------- */
/* The sheet is mounted on <body>, OUTSIDE #app (whose overflow:clip and forced
   z-index:1 on children let the page and its fixed docks paint over / trap
   it). position:fixed pins it to the whole screen above everything. */
.sheet-root {
  position: fixed;
  font-family: var(--font-family);
  color: var(--c-on-surface);
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  pointer-events: none;
}
.sheet-root--open {
  pointer-events: auto;
}
.sheet-backdrop {
  position: absolute;
  inset: 0;
  /* Dark shade + a little blur: everything behind the sheet (page content,
     the blue pay buttons, the bottom docks) drops back so the terms/orders
     text is what you read. Tapping anywhere on this closes the sheet. */
  background: rgba(15, 23, 42, 0.6);
  opacity: 0;
  transition: opacity 0.2s ease;
  touch-action: none;
}
.sheet-root--open .sheet-backdrop {
  opacity: 1;
}
.sheet {
  /* Floating liquid-glass card, inset from the edges with rounded corners on
     ALL sides (same look as the tab bar / price dock) — not a full-width
     slab attached to the bottom. */
  position: relative;
  width: calc(100% - 24px);
  max-width: 540px;
  max-height: var(--sheet-max-h, 70dvh);
  margin-bottom: max(12px, env(safe-area-inset-bottom), var(--tg-safe-area-bottom));
  display: flex;
  flex-direction: column;
  /* Frosted white, but dense (~94%): still the app's glass look, yet the
     page behind can no longer bleed through and muddy the text. */
  background: rgba(255, 255, 255, 0.97);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-2xl);
  box-shadow: 0 16px 48px rgba(15, 23, 42, 0.3);
  transform: translateY(110%);
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1);
  outline: none;
  overflow: hidden;
}
.sheet-root--open .sheet {
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .sheet, .sheet-backdrop { transition: none; }
}
.sheet__grab {
  width: 40px;
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--c-outline-variant);
  margin: 8px auto 0;
  flex-shrink: 0;
}
.sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 16px 8px 8px;
  flex-shrink: 0;
}
.sheet__title {
  font: var(--text-headline-sm);
  color: var(--c-on-surface);
  padding-inline-start: 8px;
}
.sheet__close {
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  color: var(--c-on-surface-variant);
}
.sheet__close .icon {
  width: 20px;
  height: 20px;
}
.sheet__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  touch-action: pan-y;
  padding: 4px 16px 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.sheet__footer {
  flex-shrink: 0;
  padding: 8px 12px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.sheet-loading {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: center;
  padding: 24px 0;
}
.sheet-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 28px 8px;
  text-align: center;
}
.sheet-empty__icon {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-full);
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(2, 132, 199, 0.1);
  color: var(--brand-blue);
}
.sheet-empty__icon--err {
  background: var(--c-error-container);
  color: var(--c-error);
}
.sheet-empty__icon .icon {
  width: 28px;
  height: 28px;
}
.sheet-empty__text {
  font: var(--text-body-md);
  color: var(--c-secondary);
}

/* Terms */
.terms-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.terms-list__item {
  position: relative;
  padding-inline-start: 18px;
  font: var(--text-body-md);
  line-height: 1.9;
  color: var(--c-on-surface);
}
.terms-list__item::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0.85em;
  width: 7px;
  height: 7px;
  border-radius: var(--radius-full);
  background: var(--brand-azure);
}
.terms-link {
  align-self: flex-start;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font: var(--text-label-lg);
  color: var(--brand-blue);
}
.terms-link .icon {
  width: 18px;
  height: 18px;
}
.terms-check {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 8px 12px;
  border-radius: var(--radius-lg);
  background: rgba(2, 132, 199, 0.06);
  border: 1px solid rgba(2, 132, 199, 0.18);
  cursor: pointer;
}
.terms-check input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.terms-check__box {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 7px;
  border: 2px solid var(--brand-azure);
  background: #fff;
  color: transparent;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.terms-check__box .icon {
  width: 16px;
  height: 16px;
}
.terms-check input:checked + .terms-check__box {
  background: var(--c-primary-gradient);
  color: #fff;
  border-color: transparent;
}
.terms-check input:focus-visible + .terms-check__box {
  outline: 2px solid var(--brand-azure);
  outline-offset: 2px;
}
.terms-check__text {
  font: var(--text-label-lg);
  color: var(--c-on-surface);
}
.terms-full__h {
  font: var(--text-label-lg);
  font-weight: 800;
  color: var(--brand-deep);
  margin-top: 10px;
}
.terms-full__p {
  font: var(--text-body-md);
  line-height: 2;
  color: var(--c-on-surface);
  white-space: pre-line;
}

/* Phone-verify sheet (public/js/lib/phone-verify-sheet.js) */
.phone-verify__hint {
  font: var(--text-body-md);
  line-height: 1.9;
  color: var(--c-secondary);
  margin-bottom: 14px;
}
.phone-verify__error {
  font: var(--text-body-sm);
  color: var(--c-danger, #dc2626);
  margin-top: 8px;
}
.phone-verify__resend {
  display: block;
  margin-top: 14px;
  font: var(--text-label-lg);
  color: var(--brand-blue);
  min-height: 44px;
}
.phone-verify__resend:disabled {
  color: var(--c-secondary);
  opacity: 0.7;
}
.phone-verify__links {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 14px;
}
.phone-verify__links .phone-verify__resend {
  margin-top: 0;
}
.phone-verify__change {
  font: var(--text-label-lg);
  color: var(--brand-blue);
  min-height: 44px;
}
.phone-verify__quick {
  width: 100%;
  min-height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 16px;
  border-radius: var(--radius-lg);
  background: var(--c-primary-gradient);
  color: #fff;
  font: var(--text-label-lg);
  font-weight: 700;
  margin-bottom: 14px;
}
.phone-verify__quick .icon {
  width: 18px;
  height: 18px;
}
.phone-verify__quick:disabled {
  opacity: 0.6;
}
.phone-verify__divider {
  position: relative;
  text-align: center;
  margin: 4px 0 16px;
}
.phone-verify__divider::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 50%;
  height: 1px;
  background: rgba(2, 132, 199, 0.15);
}
.phone-verify__divider span {
  position: relative;
  padding: 0 10px;
  background: var(--c-surface, #fff);
  font: var(--text-body-sm);
  color: var(--c-secondary);
}
.phone-verify__waiting {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 24px 0;
  text-align: center;
}

/* Orders list + detail */
.order-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.order-row {
  width: 100%;
  min-height: 68px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  text-align: start;
  border-radius: 20px;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.6) 100%);
  border: 1px solid var(--glass-border);
  box-shadow: 0 4px 14px -8px rgba(31, 38, 135, 0.18);
}
.order-row:active {
  background: rgba(2, 132, 199, 0.06);
}
/* Shared cute "sticker" tile — used by the orders list, the purchase hero,
   the recipient summary and the confirm-order rows, so every icon looks the
   same everywhere. A squircle with a saturated gradient, a white glyph, a
   glossy top highlight and a tiny sparkle. Colour comes from the type
   modifier (stars = gold, premium = violet, gift = pink, default = sky). */
.tile,
.order-row__icon {
  --tile-size: 44px;
  --tile-glyph: 24px;
  width: var(--tile-size);
  height: var(--tile-size);
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: calc(var(--tile-size) * 0.34);
  color: #fff;
  background: linear-gradient(140deg, #38bdf8 0%, #0ea5e9 55%, #0284c7 100%);
  box-shadow: 0 6px 12px -6px rgba(2, 132, 199, 0.65), inset 0 1.5px 0 rgba(255, 255, 255, 0.55),
    inset 0 -2px 0 rgba(3, 105, 161, 0.25);
  position: relative;
  overflow: hidden;
}
.tile::after,
.order-row__icon::after {
  content: "";
  position: absolute;
  top: calc(var(--tile-size) * 0.11);
  inset-inline-start: calc(var(--tile-size) * 0.14);
  width: calc(var(--tile-size) * 0.2);
  height: calc(var(--tile-size) * 0.11);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.55);
  transform: rotate(-28deg);
}
.tile--stars,
.order-row__icon--stars {
  background: linear-gradient(140deg, #fde047 0%, #fbbf24 55%, #f59e0b 100%);
  box-shadow: 0 6px 12px -6px rgba(217, 119, 6, 0.7), inset 0 1.5px 0 rgba(255, 255, 255, 0.6),
    inset 0 -2px 0 rgba(180, 83, 9, 0.22);
}
.tile--premium,
.order-row__icon--premium {
  background: linear-gradient(140deg, #c4b5fd 0%, #a78bfa 55%, #8b5cf6 100%);
  box-shadow: 0 6px 12px -6px rgba(109, 40, 217, 0.65), inset 0 1.5px 0 rgba(255, 255, 255, 0.55),
    inset 0 -2px 0 rgba(91, 33, 182, 0.22);
}
.tile--gift,
.order-row__icon--gift {
  background: linear-gradient(140deg, #fda4af 0%, #fb7185 55%, #f43f5e 100%);
  box-shadow: 0 6px 12px -6px rgba(225, 29, 72, 0.6), inset 0 1.5px 0 rgba(255, 255, 255, 0.55),
    inset 0 -2px 0 rgba(159, 18, 57, 0.2);
}
.tile--sky {
  /* recipient / delivery / generic rows */
  background: linear-gradient(140deg, #38bdf8 0%, #0ea5e9 55%, #0284c7 100%);
}
.tile--reaction,
.order-row__icon--reaction {
  /* warm orange/red — distinct from stars' yellow and gift's pink, reads
     as "like/heart" */
  background: linear-gradient(140deg, #fb923c 0%, #f97316 55%, #ea580c 100%);
  box-shadow: 0 6px 12px -6px rgba(194, 65, 12, 0.65), inset 0 1.5px 0 rgba(255, 255, 255, 0.55),
    inset 0 -2px 0 rgba(154, 52, 18, 0.22);
}
.tile--gold {
  /* the one "بهترین انتخاب" premium plan — warm gold to catch the eye,
     distinct from the yellow/orange .tile--stars used on the استارز tab */
  background: linear-gradient(140deg, #fde68a 0%, #f59e0b 55%, #d97706 100%);
  box-shadow: 0 6px 12px -6px rgba(180, 83, 9, 0.7), inset 0 1.5px 0 rgba(255, 255, 255, 0.6),
    inset 0 -2px 0 rgba(146, 64, 14, 0.22);
}
.tile--mint {
  background: linear-gradient(140deg, #6ee7b7 0%, #34d399 55%, #10b981 100%);
  box-shadow: 0 6px 12px -6px rgba(5, 150, 105, 0.6), inset 0 1.5px 0 rgba(255, 255, 255, 0.55),
    inset 0 -2px 0 rgba(4, 120, 87, 0.2);
}
/* A raw gift emoji standing in for the tile's usual SVG glyph (see
   summaryRow's `emoji` param) — sized/centered the same as .icon so it
   drops into the same spot without the row needing any other change. */
.tile__emoji {
  font-size: var(--tile-glyph, 24px);
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.tile--sm {
  --tile-size: 34px;
  --tile-glyph: 19px;
}
.tile--lg {
  --tile-size: 52px;
  --tile-glyph: 28px;
}
.tile .icon,
.order-row__icon .icon {
  width: var(--tile-glyph);
  height: var(--tile-glyph);
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.18));
}
.order-row__main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.order-row__title {
  font: var(--text-label-lg);
  color: var(--c-on-surface);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.order-row__meta {
  display: flex;
  gap: 4px;
  font: var(--text-label-sm);
  color: var(--c-on-surface-variant);
  white-space: nowrap;
  overflow: hidden;
}
.order-row__side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  flex-shrink: 0;
}
.order-row__price {
  font: var(--text-label-sm);
  color: var(--c-on-surface-variant);
}
.status-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: var(--radius-full);
  font: var(--text-label-sm);
  white-space: nowrap;
}
.status-badge--active { color: #b45309; background: rgba(245, 158, 11, 0.14); }
.status-badge--done { color: #047857; background: rgba(16, 185, 129, 0.14); }
.status-badge--failed { color: #b91c1c; background: rgba(239, 68, 68, 0.12); }
.order-detail .card__value {
  text-align: end;
}


/* ---------------------------------------------------------------------
   Confirm-order screen — friendlier, "cute" summary
--------------------------------------------------------------------- */
.confirm-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
  padding: 0;
}
.confirm-hero .tile {
  --tile-size: 56px;
  --tile-glyph: 30px;
  animation: confirm-pop 0.5s cubic-bezier(0.2, 1.4, 0.4, 1) both;
}
.confirm-hero__title {
  font: var(--text-headline-sm);
  font-weight: 900;
  color: var(--c-on-surface);
}
.confirm-hero__sub {
  font: var(--text-body-sm);
  color: var(--c-on-surface-variant);
}
@keyframes confirm-pop {
  0% { transform: scale(0.6) rotate(-8deg); opacity: 0; }
  100% { transform: scale(1) rotate(0); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .confirm-hero .tile { animation: none; }
}

.summary-card {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 4px 14px 12px;
  border-radius: 24px;
}
.summary-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px dashed rgba(2, 132, 199, 0.18);
}
.summary-row:last-child {
  border-bottom: none;
}
.summary-row__label {
  font: var(--text-label-sm);
  color: var(--c-secondary);
  font-weight: 600;
  white-space: nowrap;
  flex-shrink: 0;
}
/* The value takes the rest of the line and sits at its far (left) end,
   directly facing the label. */
.summary-row__value {
  flex: 1;
  min-width: 0;
  text-align: start;
  font: var(--text-label-lg);
  font-weight: 800;
  color: var(--c-on-surface);
  /* RTL row: `start` is the right edge, so push the text to the LEFT end. */
  text-align: left;
  overflow-wrap: anywhere;
}
.summary-row__value[dir="ltr"] {
  text-align: right; /* an LTR @username reads naturally, ending at the left edge */
  direction: ltr;
  unicode-bidi: plaintext;
}
/* The recipient as a plain value (no box), pinned to the LEFT edge like the
   other rows' values. The wrapper is forced LTR so "flex-start" means LEFT
   (in the page's RTL flow it meant RIGHT, which is why the card sat on the
   wrong side). Inside, the text lines are RTL/LTR again as they should be. */
.summary-row__value--person {
  display: flex;
  direction: ltr;
  justify-content: flex-start;
  overflow: hidden;
  text-align: left;
}
.summary-row__value--person .recipient-card {
  /* LTR wrapper: avatar first (left), then the name/@username beside it */
  flex-direction: row;
  gap: 8px;
  max-width: 100%;
}
.summary-row__value--person .recipient-card__lines {
  align-items: flex-start;
  text-align: left;
}
.summary-row__value--person .recipient-card__fullname {
  font-weight: 800;
  font-size: 13px;
}
.summary-row--wide {
  padding: 8px 0;
}




/* "Pay with Telegram Stars": a quiet, soft-white glass button (like the other
   secondary buttons) with only a warm hint — amber text + a small gold star
   tile — instead of a big yellow slab, so it doesn't fight the main
   «پرداخت» button for attention. */
.btn-stars {
  background: rgba(255, 255, 255, 0.7);
  color: #b45309;
  border: 1px solid rgba(245, 158, 11, 0.28);
  box-shadow: none;
}
.btn-stars .btn-icon {
  width: 18px;
  height: 18px;
  color: #f59e0b;
}
.btn-stars:active {
  background: rgba(254, 243, 199, 0.75);
}


/* Brand value on the delivery row: same weight as the others, with the brand
   colour so it reads as a selling point, not just a spec. */
.summary-row__value--brand {
  color: #059669;
}

/* The total is now a normal row (same width, same sticker, same alignment as
   the rows above), just with a bigger, coloured number. */
.summary-row__value--price {
  font: var(--text-headline-sm);
  font-weight: 900;
  color: #0369a1;
  white-space: nowrap;
}
.summary-row__unit {
  font: var(--text-label-sm);
  font-weight: 700;
  margin-inline-start: 4px;
}
.summary-hint {
  margin: 0;
  padding: 6px 0 4px;
  text-align: left;
  font: var(--text-label-sm);
  color: var(--c-on-surface-variant);
}

/* "No login · automatic · private" strip under the summary. */
.trust-strip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 18px;
  background: linear-gradient(135deg, rgba(209, 250, 229, 0.7) 0%, rgba(236, 253, 245, 0.8) 100%);
  border: 1px solid rgba(52, 211, 153, 0.35);
  margin-bottom: var(--space-md);
}
.trust-strip__text {
  font: var(--text-label-sm);
  line-height: 1.7;
  color: var(--c-on-surface-variant);
}
.trust-strip__text strong {
  display: block;
  font: var(--text-label-lg);
  font-weight: 800;
  color: #047857;
}

/* ---------------------------------------------------------------------
   Admin panel (public/js/screens/admin.js) — reuses .card, .order-list /
   .order-row / .status-badge, .btn-primary / .btn-secondary and
   .text-input from above; this section only adds what's genuinely new
   (the stat grid, filter pills, and the top-up row).
--------------------------------------------------------------------- */
.admin-stat-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gutter);
}
.admin-stat {
  background: var(--c-surface-container);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg);
  padding: var(--space-md);
  box-shadow: var(--glass-shadow);
}
.admin-stat--warn {
  border-color: rgba(245, 158, 11, 0.45);
  background: linear-gradient(160deg, rgba(245, 158, 11, 0.1), rgba(255, 255, 255, 0.6));
}
.admin-stat__label {
  font: var(--text-label-sm);
  color: var(--c-on-surface-variant);
  margin-bottom: 4px;
}
.admin-stat__value {
  font: var(--text-headline-md);
  color: var(--c-on-surface);
}
.admin-stat__value--muted {
  font: var(--text-body-md);
  color: var(--c-on-surface-variant);
}

.admin-filter-row {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 2px;
  -webkit-overflow-scrolling: touch;
}
.admin-filter-pill {
  flex: 0 0 auto;
  padding: 8px 14px;
  border-radius: var(--radius-full);
  font: var(--text-label-md);
  font-weight: 700;
  white-space: nowrap;
  background: rgba(148, 163, 184, 0.12);
  border: 1px solid rgba(100, 116, 139, 0.22);
  color: var(--c-on-surface-variant);
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.admin-filter-pill--active {
  background: rgba(0, 152, 234, 0.14);
  border-color: var(--c-primary);
  color: var(--c-primary-container);
}

.admin-tab-row {
  margin-bottom: var(--space-md);
}
.admin-tab-row .admin-filter-pill {
  flex: 1 1 0;
  text-align: center;
}

.admin-topup-row {
  display: flex;
  gap: 8px;
  align-items: center;
}
.admin-topup-row .text-input {
  flex: 1 1 auto;
}
.admin-topup-row .btn-secondary {
  flex: 0 0 auto;
  width: auto;
  padding-inline: 16px;
}

.admin-empty {
  text-align: center;
  padding: var(--space-xl) var(--space-md);
  color: var(--c-on-surface-variant);
}

.admin-section-title {
  font: var(--text-label-lg);
  color: var(--c-on-surface);
  margin: var(--space-md) 0 var(--space-xs);
}

.admin-order-detail-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: var(--space-md);
}


/* Admin → reward campaigns (screens/admin-campaigns.js). */
.admin-camp-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}
.admin-camp__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}
.admin-camp__name {
  font: var(--text-label-lg);
  font-weight: 800;
  color: var(--c-on-surface);
}
.admin-camp__bar {
  height: 6px;
  margin-top: 10px;
  border-radius: var(--radius-full);
  background: rgba(148, 163, 184, 0.2);
  overflow: hidden;
}
.admin-camp__bar > span {
  display: block;
  height: 100%;
  background: var(--c-primary);
}
.admin-camp__toggle {
  margin-top: 12px;
}
.admin-camp__hint {
  margin: 6px 0 0;
  font: var(--text-label-md);
  color: var(--c-on-surface-variant);
}
.admin-camp__warnings {
  margin: 6px 0 0;
  padding-inline-start: 18px;
}
.admin-camp-form .field-label {
  display: block;
  margin: 12px 0 6px;
}
.admin-camp-form .field-label:first-child {
  margin-top: 0;
}
.admin-camp-form__actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: var(--space-md);
}


/* Admin → prices tab (screens/admin-prices.js). */
.admin-price-rate {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: var(--space-sm);
  font: var(--text-label-md);
  color: var(--c-on-surface-variant);
}
.admin-price-refresh {
  font: var(--text-label-md);
  color: var(--c-primary-container);
  padding: 6px 10px;
  border-radius: var(--radius-full);
  background: rgba(0, 152, 234, 0.1);
}
.admin-price-muted {
  color: var(--c-on-surface-variant);
  font-weight: 400;
}
.admin-price-neg {
  color: #ef4444;
}
.admin-price-ok {
  margin-bottom: var(--space-sm);
  padding: 10px 12px;
  border-radius: var(--radius-md);
  background: rgba(34, 197, 94, 0.12);
  border: 1px solid rgba(34, 197, 94, 0.35);
  font: var(--text-label-md);
  font-weight: 700;
}
.admin-price-input-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.admin-price-input-row .input-wrap {
  flex: 1 1 auto;
  min-width: 0;
}
.admin-price-input-row .btn-primary,
.admin-price-input-row .btn-secondary {
  flex: 0 0 auto;
  width: auto;
  padding-inline: 14px;
}
.admin-price-control .field-label {
  display: block;
  margin: 12px 0 6px;
}
.admin-price-control .field-label:first-of-type {
  margin-top: 0;
}
.admin-price-live,
.admin-price-preview {
  margin-top: 6px;
  font: var(--text-label-md);
  color: var(--c-on-surface-variant);
}
.admin-price-live__title {
  font-weight: 800;
  color: var(--c-on-surface);
}
.admin-price-reward {
  margin-top: var(--space-md);
}
.admin-price-reward__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
  font: var(--text-label-lg);
  font-weight: 700;
  color: var(--c-on-surface);
}


/* Admin → SMS tab (screens/admin-sms.js): the daily-budget progress bar. */
.admin-sms-bar {
  height: 10px;
  margin: 4px 0 10px;
  border-radius: var(--radius-full);
  background: rgba(127, 127, 127, 0.18);
  overflow: hidden;
}
.admin-sms-bar__fill {
  height: 100%;
  border-radius: var(--radius-full);
  background: #22c55e;
  transition: width 0.3s ease;
}
.admin-sms-bar__fill--near {
  background: #f59e0b;
}
.admin-sms-bar__fill--full,
.admin-sms-bar__fill--off {
  background: #ef4444;
}

.admin-filter-pill:disabled {
  opacity: 0.45;
  pointer-events: none;
}

/* ---------------------------------------------------------------------
   Admin «آمار» tab (public/js/screens/admin-analytics.js) — the purchase
   funnel bars. Everything else there reuses .card / .admin-stat / pills.
--------------------------------------------------------------------- */
.admin-funnel__step + .admin-funnel__step {
  margin-top: var(--space-md);
}
.admin-funnel__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 6px;
}
.admin-funnel__label {
  font: var(--text-body-sm);
  color: var(--c-on-surface);
}
.admin-funnel__count {
  font: var(--text-headline-sm);
  color: var(--c-on-surface);
}
.admin-funnel__track {
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--c-surface-container-highest);
  overflow: hidden;
}
.admin-funnel__fill {
  height: 100%;
  min-width: 2px;
  border-radius: var(--radius-full);
  background: var(--c-primary);
  transition: width 0.4s ease;
}
.admin-funnel__pct {
  display: block;
  margin-top: 4px;
  font: var(--text-label-sm);
  color: var(--c-on-surface-variant);
}

/* Gift-sender popup (public/js/screens/confirm-order.js) */
.gift-sender-popup__img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-xl);
  background: #1c1c1e;
}
.gift-sender-popup__text {
  font: var(--text-body-md);
  color: var(--c-on-surface);
  line-height: 1.9;
}
.gift-sender-popup__actions {
  display: flex;
  flex-direction: row;
  gap: 8px;
}
.gift-sender-popup__actions > button {
  flex: 1 1 0;
  min-width: 0;
  height: 50px;
}
