/*
 * glass.css — Glassmorphism + accent-backlight design system
 *
 * Adopted by: admin/subscriptions.php
 *             admin/my_subscription_detail.php
 *             admin/dashboard.php
 *
 * Companion: assets/app.css
 *   Cascade order: <link app.css> FIRST, <link glass.css> SECOND.
 *   glass.css purposefully overrides .modal-overlay/.modal-box and a few
 *   table cell rules where they exist in app.css, so order matters.
 *
 * Token dependencies on app.css:
 *   --text, --bg, --accent, --success, --warn, --danger  (hex values, app.css owns these)
 *
 * This file declares its own RGB-triplet versions (--*-rgb) used for
 * rgba(var(--*-rgb), alpha) compositions. Triplets mirror the hex
 * values in app.css; if app.css ever changes, update them here too.
 *
 * Themes: html[data-theme="dark"] (default) and html[data-theme="light"].
 * Light overrides are placed ADJACENT to their dark counterparts, not
 * collected in a separate block — single-source maintenance.
 *
 * AUTO-DECISION: html[data-theme="light"] (not bare [data-theme="light"])
 *   for selector-form consistency with app.css. Specificity tie matters
 *   when extra root-level selectors get layered later.
 *
 * Naming:
 *   .glass-*       — surface containers (card, row, divider, …)
 *   .btn-glass     — buttons with backlight glow
 *   .badge-glass   — pill badges with status-coloured borders
 *   Other classes are subcomponents (kebab-case under their parent).
 */

/* ───────────────────────── 1. RGB triplet tokens ───────────────────────── */
/* RGB-triplet tokens (--accent-rgb / --success-rgb / --warn-rgb / --danger-rgb)
   are defined in app.css for both dark and light themes in comma-separated
   form, so every `rgba(var(--*-rgb), alpha)` call site in glass.css resolves
   correctly. Earlier versions of this file redefined those tokens with
   space-separated values, which broke legacy `rgba(...,a)` consumers. */

/* ───────────────────────── 1a. Theme tokens + a11y primitives ───────────── */
/* UI-0035: themed token for sticky table headers. Was a hardcoded RGBA literal
   in dashboard.php that painted opaquely over operator-set custom backgrounds.
   Default (and the dark theme) use the dark surface; light theme overrides. */
:root, html[data-theme="dark"] { --surface-thead-sticky: rgba(20,22,32,0.85); }
html[data-theme="light"]       { --surface-thead-sticky: rgba(255,255,255,0.88); }

/* UI-0003: site-wide keyboard focus ring. Many controls set outline:none on
   :focus and replace it with a box-shadow ring; those keep their own ring
   (higher specificity). This adds a visible token-based outline to every other
   focusable surface (links, buttons, row-as-link, badges, menu items, anything
   with [tabindex]) so keyboard users never lose the focus indicator (WCAG
   2.4.7). Outline (not box-shadow) so it never fights existing shadow rings. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible,
[role="button"]:focus-visible,
[role="menuitem"]:focus-visible,
[role="radio"]:focus-visible,
.glass-row:focus-visible{
  outline: 2px solid rgba(var(--accent-rgb), 0.70);
  outline-offset: 2px;
  border-radius: 8px;
}

/* UI-0004: skip-to-content link. Visually hidden until focused, then pinned to
   the top so keyboard users can bypass the brand + nav + impersonate bar. */
.skip-link{
  position: absolute;
  inset-inline-start: 12px;
  top: -60px;
  z-index: 2000;
  padding: 10px 16px;
  border-radius: 0 0 12px 12px;
  background: rgba(var(--accent-rgb), 0.95);
  color: #fff;
  font-weight: 600;
  text-decoration: none;
  transition: top 0.15s ease;
}
.skip-link:focus{
  top: 0;
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* ───────────────────────── 1b. Form-control box-sizing reset ─────────────
   Every `.glass-*` form control declares `width: 100%` plus inner padding.
   Without box-sizing: border-box the padding adds to the 100% and the
   control overflows its parent — visible most cleanly on the wallet
   top-up modal's amount input (which carries 20 px horizontal padding +
   1.5rem font, so the overflow is ~40 px at 320 px viewport widths).
   Setting box-sizing here once covers every glass control without
   needing per-rule edits. */
.glass-amount-input,
.glass-textarea,
.glass-select,
.glass-input-group input,
.glass-input-group textarea,
.glass-stepper .stepper-input,
.form-control,
.btn-glass.block{
  box-sizing: border-box;
}

/* ───────────────────────── 2. Glass card ─────────────────────────────── */
.glass-card{
  background: rgba(20,22,32,0.55);
  backdrop-filter: blur(var(--glass-blur-card, 20px)) saturate(1.3);
  -webkit-backdrop-filter: blur(var(--glass-blur-card, 20px)) saturate(1.3);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 18px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.05);
  padding: 20px;
}
/* RULE-9 (Phase 6): .sm modifier — slim card variant for filter/control bars
   and inline notification cards. Smaller radius + tighter padding. */
.glass-card.sm{
  padding: 12px 14px;
  border-radius: 14px;
}
html[data-theme="light"] .glass-card{
  background: rgba(255,255,255,0.65);
  border-color: rgba(0,0,0,0.06);
  box-shadow: 0 8px 32px rgba(0,0,0,0.08), inset 0 1px 0 rgba(255,255,255,0.6);
}

/* ───────────────────────── 3. Backlight button ───────────────────────── */
.btn-glass{
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: rgba(255,255,255,0.05);
  backdrop-filter: blur(var(--glass-blur-control, 10px));
  -webkit-backdrop-filter: blur(var(--glass-blur-control, 10px));
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 12px;
  padding: 12px 20px;
  font-weight: 600; font-size: 0.9rem; font-family: inherit;
  color: var(--text);
  cursor: pointer; text-decoration: none;
  transition: transform 0.25s ease, border-color 0.25s ease;
  z-index: 1; min-height: 44px;
}
.btn-glass::before{
  content:""; position:absolute; inset:-4px;
  border-radius: inherit;
  background: radial-gradient(circle at center, var(--btn-glow-color, transparent), transparent 70%);
  filter: blur(20px); opacity: 0;
  transition: opacity 0.3s ease;
  z-index: -1; pointer-events: none;
}
.btn-glass:hover { transform: translateY(-1px); border-color: rgba(255,255,255,0.18); }
.btn-glass:hover::before { opacity: 0.85; }
.btn-glass:active { transform: translateY(0); }
.btn-glass:focus-visible { outline: 2px solid rgba(255,255,255,0.30); outline-offset: 2px; }
html[data-theme="light"] .btn-glass{
  background: rgba(255,255,255,0.65);
  border-color: rgba(0,0,0,0.08);
}
html[data-theme="light"] .btn-glass:hover { border-color: rgba(0,0,0,0.16); }

.btn-glass.primary { --btn-glow-color: rgba(var(--accent-rgb), 0.55); }
.btn-glass.success { --btn-glow-color: rgba(var(--success-rgb), 0.55); }
.btn-glass.warn    { --btn-glow-color: rgba(var(--warn-rgb), 0.55); }
.btn-glass.danger  { --btn-glow-color: rgba(var(--danger-rgb), 0.55); }
.btn-glass.neutral { --btn-glow-color: transparent; }

/* AUTO-DECISION: confirm-delete inside modal gets a stronger glow so the
   destructive action visually announces itself before the click. */
.btn-glass.danger.confirm::before { filter: blur(28px); }
.btn-glass.danger.confirm:hover::before { opacity: 1; }

.btn-glass.block { display: flex; width: 100%; }
.btn-glass.sm    { padding: 8px 14px; min-height: 36px; font-size: 0.82rem; }

/* ───────────────────────── 4. Glass badge ────────────────────────────── */
.badge-glass{
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 4px 12px;
  border-radius: 999px;
  font-size: 0.78rem; font-weight: 600; line-height: 1;
  background: rgba(255,255,255,0.06);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--badge-border-color, rgba(255,255,255,0.12));
  color: var(--badge-text-color, var(--text));
}
.badge-glass.active  { --badge-border-color: rgba(var(--success-rgb), 0.5); --badge-text-color: rgb(74,222,128); }
.badge-glass.warn    { --badge-border-color: rgba(var(--warn-rgb), 0.5);    --badge-text-color: rgb(252,211,77); }
.badge-glass.danger  { --badge-border-color: rgba(var(--danger-rgb), 0.5);  --badge-text-color: rgb(248,113,113); }
.badge-glass.deleted { --badge-border-color: rgba(156,163,175,0.5);        --badge-text-color: rgb(209,213,219); }
.badge-glass.neutral { --badge-border-color: rgba(var(--accent-rgb), 0.4);  --badge-text-color: rgb(165,180,252); }
/* Light-theme text colours — the dark-mode pales above fail WCAG AA contrast
   against the white card the badge sits on. Mirrors .glass-tx-badge's
   light-theme overrides (see §32). */
html[data-theme="light"] .badge-glass.active  { --badge-text-color: rgb(22,101,52); }
html[data-theme="light"] .badge-glass.warn    { --badge-text-color: rgb(146,64,14); }
html[data-theme="light"] .badge-glass.danger  { --badge-text-color: rgb(185,28,28); }
html[data-theme="light"] .badge-glass.deleted { --badge-text-color: rgb(75,85,99); }
html[data-theme="light"] .badge-glass.neutral { --badge-text-color: rgb(67,56,202); }

@keyframes pulse-glow {
  0%, 100% { box-shadow: 0 0 0 0 rgba(var(--success-rgb), 0.40); }
  50%      { box-shadow: 0 0 0 6px rgba(var(--success-rgb), 0); }
}
/* AUTO-DECISION: only .active pulses — drawing the eye to the live state
   is the design intent. Other states are intentionally static. */
.badge-glass.active { animation: pulse-glow 2.4s ease-in-out infinite; }

/* ───────────────────────── 5. Glass row (list) ───────────────────────── */
.glass-row{
  display: grid;
  grid-template-columns: 64px 1fr auto auto auto 24px;
  gap: 16px;
  align-items: center;
  padding: 14px 18px;
  background: rgba(255,255,255,0.03);
  backdrop-filter: blur(var(--glass-blur-row, 12px));
  -webkit-backdrop-filter: blur(var(--glass-blur-row, 12px));
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 14px;
  margin-bottom: 10px;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.15s ease;
  cursor: pointer; text-decoration: none; color: inherit;
}
/* AUTO-DECISION: entire row clickable (anchor wrapper). Simpler than a
   per-row dropdown, friendlier on mobile where small action affordances
   regress. Actions live on the detail page. */
.glass-row:hover{
  background: rgba(255,255,255,0.06);
  border-color: rgba(255,255,255,0.12);
  /* AUTO-DECISION: small leftward nudge feels like a hover tick — physical
     coords (transform is direction-agnostic) chosen deliberately. */
  transform: translateX(-2px);
}
html[data-theme="light"] .glass-row{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.05);
}
html[data-theme="light"] .glass-row:hover{
  background: rgba(0,0,0,0.05);
  border-color: rgba(0,0,0,0.10);
}
.glass-row .row-id   { font-family: ui-monospace, monospace; opacity: 0.55; font-size: 0.82rem; }
.glass-row .row-name { font-weight: 600; }
.glass-row .row-pg   { font-family: ui-monospace, monospace; font-size: 0.78rem; opacity: 0.7; direction: ltr; }
.glass-row .row-vol  { font-size: 0.85rem; opacity: 0.85; white-space: nowrap; }
.glass-row .row-date { font-size: 0.80rem; opacity: 0.60; white-space: nowrap; }
.glass-row .row-chev { opacity: 0.4; font-size: 1.1rem; }
@media (max-width: 720px) {
  .glass-row { grid-template-columns: auto 1fr auto; row-gap: 6px; padding: 12px 14px; }
  .glass-row .row-id   { grid-row: 1; grid-column: 1; }
  .glass-row .row-name { grid-row: 1; grid-column: 2; }
  /* The status badge shares column 3 with .row-date; the (wider) date sizes the
     column, so the auto-placed badge would stretch (default justify-self) and its
     centred text would drift to the inline-start edge. Pin it and hug its content
     so the pill stays content-width with the label centred. */
  .glass-row .badge-glass { grid-row: 1; grid-column: 3; justify-self: end; }
  .glass-row .row-pg   { grid-row: 2; grid-column: 1 / -1; }
  .glass-row .row-vol  { grid-row: 3; grid-column: 1 / 3; }
  .glass-row .row-date { grid-row: 3; grid-column: 3; }
  .glass-row .row-chev { display: none; }
}

/* ───────────────────────── 6. Filters bar ────────────────────────────── */
.filters-glass{
  display: flex; gap: 12px;
  padding: 14px 18px;
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 14px;
  backdrop-filter: blur(var(--glass-blur-row, 12px));
  -webkit-backdrop-filter: blur(var(--glass-blur-row, 12px));
  margin-bottom: 18px;
  flex-wrap: wrap;
  align-items: flex-end;
}
.filters-glass label { display: block; font-size: 0.78rem; opacity: 0.7; margin-bottom: 4px; }
.filters-glass input,
.filters-glass select{
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 10px;
  padding: 8px 12px;
  color: var(--text); font-size: 0.9rem; font-family: inherit;
}
.filters-glass input:focus,
.filters-glass select:focus{
  outline: none;
  border-color: rgba(var(--accent-rgb), 0.5);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.15);
}
html[data-theme="light"] .filters-glass{
  background: rgba(0,0,0,0.025); border-color: rgba(0,0,0,0.05);
}
html[data-theme="light"] .filters-glass input,
html[data-theme="light"] .filters-glass select{
  background: #fff; border-color: rgba(0,0,0,0.10);
}

/* ─────────────────── 6b. Glass filter bar (in-card) ───────────────────── */
/* Canonical filter/control ROW for forms that live INSIDE a .glass-card
   (the card already supplies the chrome — unlike .filters-glass above, which
   is the self-chromed standalone variant). Aligns labelled .glass-input-group
   controls and trailing .btn-glass actions onto a single baseline with a
   shared 44px height, so a label-less button never floats off the line and
   every control reads as one row.

   Why this exists: a .glass-input-group carries a default margin-bottom:12px;
   dropped into an align-items:flex-end row next to a bare button, that margin
   lifts the inputs 12px above the button's baseline (the exact misalignment
   this class neutralises). The selects/inputs and the button are also forced
   to the same 44px height. Layout-only — no markup semantics, names, ids, or
   behaviour change. Mirrors the page-scoped .search-form pattern. */
.glass-filter-bar{
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px;
}
.glass-filter-bar .glass-input-group{
  margin-bottom: 0;
  flex: 1 1 160px;   /* equal-width fields that share the bar and fill it when wrapped */
  min-width: 0;      /* permit shrink below intrinsic width so wrapping is clean */
}
.glass-filter-bar .glass-input,
.glass-filter-bar .glass-select{ min-height: 44px; }
.glass-filter-bar .btn-glass{ flex: 0 0 auto; min-height: 44px; }
@media (max-width: 560px){
  /* Narrow screens: trailing action buttons take their own full-width row so
     they never wedge beside a half-width field. */
  .glass-filter-bar .btn-glass{ flex: 1 1 100%; width: 100%; }
}

/* ───────────────────────── 7. URL copy box ───────────────────────────── */
/* fixes-merged-02: drop the explicit copy button — the card number /
   crypto address are one-tap-selectable via user-select: all. Row
   stays a flex container only so the layout matches other elements
   that might still include a trailing meta chip. */
.url-copy-glass{
  display: flex; align-items: center; gap: 8px;
  padding: 10px 14px;
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  /* A-L: no flex-wrap — children own `min-width: 0` + `overflow: hidden`
     + `text-overflow: ellipsis`, so the URL ellipsizes instead of pushing
     the copy button onto a second line on narrow viewports. */
  flex-wrap: nowrap;
  min-width: 0;
}
.url-copy-glass > .url-text,
.url-copy-glass > .card-number,
.url-copy-glass > .crypto-address{
  flex: 1 1 auto;
  min-width: 0;
  /* user-select: all — a single click/tap selects the entire string so
     the user can ⌘C / Ctrl-C / mobile-select it without an explicit
     copy affordance. The card-number / crypto-address already render
     with monospace + dir:ltr; this just adds the selection behaviour. */
  -webkit-user-select: all;
  -moz-user-select: all;
  user-select: all;
  cursor: text;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.url-copy-glass > button,
.url-copy-glass > .btn-glass{
  flex-shrink: 0;
}
.url-copy-glass .url-text{
  font-family: ui-monospace, monospace;
  font-size: 0.82rem;
  direction: ltr; color: var(--text);
}
html[data-theme="light"] .url-copy-glass{
  background: rgba(0,0,0,0.03); border-color: rgba(0,0,0,0.08);
}

/* ───────────────────────── 7b. Usage progress bar ────────────────────── */
/* H4.3: per-row usage bar used in admin/my_subscriptions.php for the
   "مصرف" column. Width of the outer track is set inline by the caller
   (e.g. style="width:70px"); the inner <span> width is the percentage.
   Inline-axis is RTL-aware: anchoring the fill via `inset-inline-start`
   keeps it growing from the row's leading edge in both LTR and RTL. */
.usage-bar{
  position: relative;
  display: inline-block;
  height: 6px;
  max-width: 100%;
  min-width: 40px;
  border-radius: 999px;
  background: rgba(255,255,255,0.08);
  overflow: hidden;
  vertical-align: middle;
}
.usage-bar > span{
  position: absolute;
  top: 0; bottom: 0;
  inset-inline-start: 0;
  display: block;
  /* UI-0112: fill width comes from a CSS custom property set in the markup
     (style="--usage-pct: NN%"), not an inline width declaration — keeps the
     bar working under a strict style-src CSP that forbids `width:%`. */
  width: var(--usage-pct, 0%);
  max-width: 100%;
  border-radius: 999px;
  background: linear-gradient(
    90deg,
    rgba(var(--accent-rgb), 0.85),
    rgba(var(--accent-rgb), 0.55));
  transition: width .25s var(--ease, ease);
}
html[data-theme="light"] .usage-bar{
  background: rgba(0,0,0,0.08);
}

/* ───────────────────────── 8. QR card ────────────────────────────────── */
.qr-glass{
  display: flex; flex-direction: column; align-items: center;
  padding: 24px;
  border-radius: 18px;
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.10);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  box-shadow: 0 0 40px rgba(255,255,255,0.04);
}
/* QR image: must NOT be rounded — rounded corners clip the finder patterns
   and quiet zone, breaking scanner detection. White background and padding
   double as the QR quiet-zone (≥4 modules), so keep them. Size bumped from
   200 → 256 so modules land closer to whole-pixel boundaries at common DPRs
   (image is rendered as ~294–342px SVG natively). */
.qr-glass img{
  width: 288px; height: 288px;
  background: #fff; padding: 0;
  border-radius: 0;
  box-sizing: content-box;
  /* Native SVG (scale=10) is ~430–530px → display always downsamples.
     Combined with shape-rendering="auto" on the SVG, downsample produces
     uniform anti-aliased module edges. The hints below have no negative
     effect on SVG-in-img and stay for safety per scan-quality checklist. */
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}
.qr-glass .qr-caption{ margin-top: 12px; font-size: 0.85rem; opacity: 0.7; }

/* QR zoom modal — desktop ≈ one-third of viewport, mobile full-screen.
   Same scanner-safety rules apply: image stays square-cornered and sits on
   a white tile so the quiet zone is preserved. */
.modal-overlay.glass-modal.qr-zoom-modal .modal-box{
  width: clamp(360px, 36vw, 560px);
  max-width: calc(100vw - 24px);
  padding: 20px;
}
.qr-zoom-body{
  display: flex; flex-direction: column; align-items: center;
  padding: 8px 0 4px;
}
.qr-zoom-img{
  width: 100%;
  max-width: 480px;
  height: auto;
  aspect-ratio: 1 / 1;
  /* UI-0371: in narrow landscape the 1:1 aspect made height follow width and
     overflow the short viewport. Clamp by viewport height (dvh + vh fallback)
     so the square shrinks to fit instead of spilling. */
  max-height: 80vh;
  max-height: 80dvh;
  background: #fff;
  padding: 0;
  border-radius: 0;
  box-sizing: content-box;
  /* See comment on .qr-glass img — native SVG is much larger than display,
     so downsampling + shape-rendering="auto" yields clean uniform modules. */
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}
.qr-zoom-caption{ margin-top: 14px; font-size: 0.9rem; opacity: 0.75; }

/* Mobile = full-screen (opts out of the global bottom-sheet style applied
   to all .glass-modal at ≤768px further down in this file). */
@media (max-width: 768px){
  .modal-overlay.glass-modal.qr-zoom-modal{
    align-items: center;
    padding: 0;
  }
  .modal-overlay.glass-modal.qr-zoom-modal .modal-box{
    width: 100vw;
    max-width: 100vw;
    /* A-M: 100vh fallback for iOS Safari <15.4 / Chrome <108. */
    height: 100vh;
    height: 100dvh;
    max-height: 100vh;
    max-height: 100dvh;
    margin: 0;
    border-radius: 0;
    border-inline: 0;
  }
  .qr-zoom-body{
    flex: 1;
    justify-content: center;
    padding: 16px;
  }
  .qr-zoom-img{ max-width: min(90vmin, 540px); }
}

/* ───────────────────────── 9. Modal (glass) ──────────────────────────── */
/* fixes-merged-02 / Issue 1: the modal needs a single, reliable scroll
   container. Pre-fix the .modal-box inherited overflow-y:auto + max-height:
   90vh from app.css §20, but glass.css §9 redeclared the box without
   re-asserting those rules, and the mobile bottom-sheet at the bottom of
   this file capped to 92vh. On mobile, 1vh > visible vh while the URL bar
   is showing, so the bottom of the modal lived BELOW the visible area and
   no scroll affordance could reach it. The dvh unit handles the URL-bar
   case; flex + min-height:0 + overflow:hidden on the overlay restricts
   scrolling to the box; overscroll-behavior:contain prevents body-scroll
   leak when the touch reaches the box's edge. */
@keyframes modal-in {
  0%   { opacity: 0; transform: scale(0.96); }
  100% { opacity: 1; transform: scale(1); }
}
.modal-overlay.glass-modal{
  /* Closed by default — opened via .open / .active / [data-open="true"]
     which app.js's openModal() toggles. Pre-fix, this selector was
     display:flex (always-on) with no position:fixed/z-index, so every
     modal rendered permanently inline at the bottom of the document.
     Page-scoped workarounds existed on representatives.php and
     deposits.php; this rule supersedes them. */
  display: none;
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(0,0,0,0.6);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  /* Overlay itself never scrolls; the box does. Without overflow:hidden
     here the page can scroll behind the open modal on iOS Safari. */
  align-items: center;
  justify-content: center;
  overflow: hidden;
  /* dvh = dynamic viewport height: collapses by the URL bar's height on
     mobile, unlike vh which references the largest viewport. A-M:
     100vh precedes 100dvh so iOS Safari <15.4 / Chrome <108 still
     pin the overlay to the viewport. */
  height: 100vh;
  height: 100dvh;
  max-height: 100vh;
  max-height: 100dvh;
}
.modal-overlay.glass-modal.open,
.modal-overlay.glass-modal.active,
.modal-overlay.glass-modal[data-open="true"]{
  display: flex;
}
.modal-overlay.glass-modal.open .modal-box,
.modal-overlay.glass-modal.active .modal-box,
.modal-overlay.glass-modal[data-open="true"] .modal-box{
  animation: modal-in 220ms ease-out;
}
.modal-overlay.glass-modal .modal-box{
  /* fixes-merged-02 / Section D: opacity lowered from 0.85 to 0.62 so the
     body blobs (\u00a750) actually filter through the modal panel \u2014 otherwise
     the modal reads as a flat dark sheet and the "glass" treatment is
     meaningless. backdrop-filter blur(24px)+saturate(1.4) does the heavy
     lifting; the modal still reads as solid-enough that long-form content
     stays readable. */
  background: rgba(18,20,30,0.62);
  backdrop-filter: blur(28px) saturate(1.5);
  -webkit-backdrop-filter: blur(28px) saturate(1.5);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 18px;
  box-shadow:
    0 24px 64px rgba(0,0,0,0.55),
    inset 0 1px 0 rgba(255,255,255,0.06);
  /* fixes-merged-02 / Issue 1: scroll container. Flex column + min-height:0
     lets long-content children shrink so overflow-y:auto on the box itself
     actually scrolls. dvh handles the mobile URL-bar gap. */
  display: flex;
  flex-direction: column;
  min-height: 0;
  /* UI-0044: canonical modal-box padding. Page modals override this with
     ID-scoped rules (higher specificity), so existing modals are unchanged;
     this gives any future generic .modal-box a sane default AND a real basis
     for the .modal-actions full-bleed `margin: 0 -24px` below (which used to
     cancel a padding that did not exist). */
  padding: 22px 24px;
  /* A-M: 100vh fallback before 100dvh for iOS Safari <15.4. */
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.modal-overlay.glass-modal .modal-header{
  /* Header never compresses when the body grows. */
  flex-shrink: 0;
  /* Canonical header layout: title on one side, the ✕ close control on the
     other. Page modals that ship an id-scoped .modal-header (deposits.php,
     subscription_detail.php) or an inline style (payment_methods.php) win on
     specificity and stay byte-for-byte unchanged; this gives the modals that
     DON'T (wallet top-up, representatives, my_subscription_detail) the same
     row instead of a stacked, default-flow header. */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 0;
}
.modal-overlay.glass-modal .modal-header h3{ margin: 0; }

/* Canonical glass close control — the icon-only "✕" button in every glass
   modal header. Without this the bare <button> fell back to raw UA chrome
   (grey box, default font) and clashed with the glass theme; that was the
   wallet top-up complaint. The same declarations were duplicated id-scoped
   in deposits.php / subscription_detail.php — hoisting them to the design
   system makes the wallet top-up, representatives and my_subscription_detail
   modals match too. Scoped :not(.btn-glass) so a "لغو/Cancel" button that
   only borrows .modal-close for its close BEHAVIOUR keeps its .btn-glass
   styling. Purely visual — the JS hook (app.js binds '.modal-close') is
   untouched, so every close button still works. */
.modal-close:not(.btn-glass){
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 8px;
  color: var(--text);
  font-family: inherit;
  font-size: 1.15rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s ease;
}
.modal-close:not(.btn-glass):hover{ background: rgba(255,255,255,0.08); }
html[data-theme="light"] .modal-close:not(.btn-glass):hover{ background: rgba(0,0,0,0.06); }
html[data-theme="light"] .modal-overlay.glass-modal .modal-box{
  background: rgba(255,255,255,0.92);
  border-color: rgba(0,0,0,0.06);
  box-shadow: 0 24px 64px rgba(0,0,0,0.18);
}

/* ───────────────────────── 10. Layout utilities ──────────────────────── */
/* A-E: bare .page-head rules deleted — every admin page uses
   .glass-page-head (the canonical declaration is just below at §10b),
   and the duplicate here + app.css:365 combined kept producing
   inconsistent alignment depending on cascade order. */

.empty-glass{ text-align: center; padding: 56px 24px; }
.empty-glass .empty-icon { font-size: 2.6rem; opacity: 0.6; margin-bottom: 12px; }
.empty-glass h3 { margin: 0 0 6px; font-weight: 600; }
.empty-glass p  { margin: 0; opacity: 0.65; font-size: 0.88rem; }

/* UI-0511: single-column mobile track MUST be minmax(0,1fr), not the implicit
   `auto`. With an `auto` track the column's min size = its min-content size,
   and the non-wrapping subscription URL in .url-copy-glass (white-space:nowrap)
   has a min-content as wide as the full URL (~800px) — blowing the cards far
   past the viewport on phones. The `html,body{overflow-x:hidden}` guard then
   CLIPS the spill instead of scrolling it, so on iPhone 12 Pro Max the page
   looked "zoomed-in / cut off". minmax(0,1fr) lets the track shrink to the
   viewport so the URL ellipsizes (it already has min-width:0 + overflow:hidden).
   The ≥900px two-column rule overrides this and is unaffected. */
.detail-grid{ display: grid; gap: 18px; grid-template-columns: minmax(0, 1fr); }
.detail-grid > * { min-width: 0; }
@media (min-width: 900px) {
  .detail-grid { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); align-items: start; }
}

.info-kv{ display: grid; grid-template-columns: 1fr 1fr; gap: 14px 22px; font-size: 0.9rem; }
.info-kv .k { opacity: 0.6; font-size: 0.78rem; margin-bottom: 2px; }
.info-kv .v { font-weight: 600; }
@media (max-width: 480px) { .info-kv { grid-template-columns: 1fr; } }

.action-stack{ display: flex; flex-direction: column; gap: 10px; }

.glass-divider{
  height: 1px;
  background: linear-gradient(to left, transparent, rgba(255,255,255,0.10), transparent);
  margin: 16px 0;
}
html[data-theme="light"] .glass-divider{
  background: linear-gradient(to left, transparent, rgba(0,0,0,0.08), transparent);
}

.refund-preview{
  background: rgba(var(--success-rgb), 0.08);
  border: 1px solid rgba(var(--success-rgb), 0.30);
  border-radius: 12px;
  padding: 14px 16px;
  margin-bottom: 12px;
  font-size: 0.9rem; line-height: 1.7;
}
.refund-preview .small { display: block; margin-top: 6px; font-size: 0.78rem; opacity: 0.65; }

.card-title{
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin: 0 0 14px; font-size: 1.05rem; font-weight: 700;
}
.card-title .sub-id{ font-family: ui-monospace, monospace; opacity: 0.6; font-weight: 500; }

.wallet-balance{ font-size: 1.6rem; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.wallet-balance.low{ color: rgb(var(--warn-rgb)); }
.wallet-hint{ font-size: 0.8rem; opacity: 0.65; margin-top: 4px; }

.bcrumb{ display: flex; align-items: center; gap: 8px; font-size: 0.85rem; opacity: 0.75; margin-bottom: 12px; flex-wrap: wrap; }
.bcrumb a{ color: inherit; text-decoration: none; }
.bcrumb a:hover{ text-decoration: underline; }
.bcrumb .sep{ opacity: 0.4; }

/* ═══════════════════════════════════════════════════════════════════════
   DASHBOARD EXTENSIONS — added in Phase 3 of the dashboard refactor session
   ═══════════════════════════════════════════════════════════════════════ */

/* ───────────────────────── 11. Glass stat card ───────────────────────── */
.glass-stat-card{
  position: relative;
  display: flex; flex-direction: column; gap: 6px;
  padding: 20px 22px;
  background: rgba(20,22,32,0.55);
  backdrop-filter: blur(20px) saturate(1.3);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 18px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.05);
  overflow: hidden;
  isolation: isolate;
}
html[data-theme="light"] .glass-stat-card{
  background: rgba(255,255,255,0.65);
  border-color: rgba(0,0,0,0.06);
  box-shadow: 0 8px 32px rgba(0,0,0,0.08), inset 0 1px 0 rgba(255,255,255,0.6);
}

/* AUTO-DECISION: accent strip uses inset-inline-start (NOT inset-inline-end).
   In RTL the inline-start direction is the right edge — i.e. the leading
   edge of the reading direction. The brief's wording said "leading edge
   (right side in RTL)" but specified inset-inline-end, which would put
   the strip on the trailing edge. We honor the stated visual intent. */
.glass-stat-card::before{
  content:"";
  position: absolute;
  inset-block: 12%;
  inset-inline-start: 0;
  width: 3px;
  border-radius: 999px;
  background: var(--stat-accent, transparent);
  box-shadow: 0 0 12px var(--stat-accent, transparent);
}

/* Diffused glow behind the card */
.glass-stat-card::after{
  content:"";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 80% 50%, var(--stat-accent-soft, transparent), transparent 60%);
  opacity: 0.4;
  z-index: -1;
  pointer-events: none;
}

.glass-stat-card .stat-icon { font-size: 1.6rem; opacity: 0.85; line-height: 1; }
.glass-stat-card .stat-val  { font-size: 2rem; font-weight: 800; line-height: 1.1; }
.glass-stat-card .stat-lbl  { font-size: 0.85rem; opacity: 0.7; }
.glass-stat-card .stat-sub  { font-size: 0.78rem; opacity: 0.55; margin-top: 4px; }
.glass-stat-card a          { color: inherit; text-decoration: none; display: contents; }

/* AUTO-DECISION: 6 variants cover the dashboard's 6 source colors plus a
   neutral default. info ≈ blue/cyan, purple kept distinct from accent. */
.glass-stat-card.accent  { --stat-accent: rgb(var(--accent-rgb));  --stat-accent-soft: rgba(var(--accent-rgb), 0.12); }
.glass-stat-card.success { --stat-accent: rgb(var(--success-rgb)); --stat-accent-soft: rgba(var(--success-rgb), 0.12); }
.glass-stat-card.warn    { --stat-accent: rgb(var(--warn-rgb));    --stat-accent-soft: rgba(var(--warn-rgb), 0.12); }
.glass-stat-card.danger  { --stat-accent: rgb(var(--danger-rgb));  --stat-accent-soft: rgba(var(--danger-rgb), 0.12); }
.glass-stat-card.info    { --stat-accent: rgb(96, 165, 250);       --stat-accent-soft: rgba(96, 165, 250, 0.12); }
.glass-stat-card.purple  { --stat-accent: rgb(168, 85, 247);       --stat-accent-soft: rgba(168, 85, 247, 0.12); }

/* AUTO-DECISION: dashboard's .stat-grid container (defined in app.css)
   already handles the responsive grid. We override only its mobile gap
   for visual harmony with .actions-toolbar. */
.stat-grid { gap: 14px; }
/* UI-0023: status-filter tiles wrap an <a> around .glass-stat-card; surface a
   visible keyboard focus ring on the card when its anchor is focused. */
.stat-grid a:focus-visible { outline: none; }
.stat-grid a:focus-visible .glass-stat-card {
  outline: 2px solid rgba(var(--accent-rgb), 0.65);
  outline-offset: 2px;
}
/* UI-0116: mark the active status-filter tile (the link carries
   aria-current="page") so the current filter is visually distinguished. */
.stat-grid a[aria-current="page"] .glass-stat-card {
  outline: 2px solid rgba(var(--accent-rgb), 0.55);
  outline-offset: 2px;
}
@media (max-width: 480px) {
  .glass-stat-card { padding: 18px; }
  .glass-stat-card .stat-val { font-size: 1.7rem; }
}

/* ───────────────────────── 12. Leaderboard table ─────────────────────── */
/* AUTO-DECISION: scoped to .glass-card descendants so it doesn't bleed
   into the legacy tables in other admin pages still using app.css. */
.glass-card .leaderboard{
  width: 100%;
  border-collapse: collapse;
  background: transparent;
}
.glass-card .leaderboard th{
  text-align: start; /* RTL leading edge (logical: right in RTL, left in LTR) */
  padding: 10px 12px;
  font-size: 0.82rem; font-weight: 600;
  opacity: 0.6;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  white-space: nowrap;
}
.glass-card .leaderboard td{
  padding: 12px;
  border-bottom: 1px solid rgba(255,255,255,0.04);
  font-size: 0.9rem;
}
.glass-card .leaderboard tr:last-child td { border-bottom: none; }
.glass-card .leaderboard tr { transition: background 0.2s ease; }
.glass-card .leaderboard tbody tr:hover { background: rgba(255,255,255,0.03); }

html[data-theme="light"] .glass-card .leaderboard th { border-bottom-color: rgba(0,0,0,0.08); }
html[data-theme="light"] .glass-card .leaderboard td { border-bottom-color: rgba(0,0,0,0.05); }
html[data-theme="light"] .glass-card .leaderboard tbody tr:hover { background: rgba(0,0,0,0.03); }

.glass-card .leaderboard .lb-rank{
  display: inline-flex;
  align-items: center; justify-content: center;
  width: 28px; height: 28px;
  border-radius: 50%;
  background: rgba(255,255,255,0.06);
  font-weight: 700; font-size: 0.85rem;
}
/* AUTO-DECISION: gold/silver/bronze hardcoded RGB rather than tokens —
   medal colors are universally recognized and need to stay consistent
   across themes; tying them to brand tokens would weaken the metaphor.
   gold = warn token, silver = neutral gray, bronze = amber-orange. */
.glass-card .leaderboard tbody tr:nth-child(1) .lb-rank { background: rgba(var(--warn-rgb), 0.25);     color: rgb(252, 211, 77); }
.glass-card .leaderboard tbody tr:nth-child(2) .lb-rank { background: rgba(156, 163, 175, 0.25);      color: rgb(229, 231, 235); }
.glass-card .leaderboard tbody tr:nth-child(3) .lb-rank { background: rgba(217, 119, 6, 0.25);        color: rgb(251, 146, 60); }

.glass-card .leaderboard .lb-num{ font-variant-numeric: tabular-nums; }

/* ───────────────────────── 13. Quick-actions toolbar ─────────────────── */
.actions-toolbar{
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 14px 18px;
}
.actions-toolbar .btn-glass{
  padding: 10px 16px;
  font-size: 0.9rem;
  min-height: 40px; /* tighter than the standard 44px since these are secondary actions */
}

/* ───────────────────────── 14. Glass page header ─────────────────────── */
.glass-page-head{
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  padding: 18px 22px;
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 16px;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  margin-bottom: 18px;
}
.glass-page-head .head-title { font-size: 1.4rem; font-weight: 700; margin: 0; }
.glass-page-head .head-sub   { font-size: 0.9rem; opacity: 0.7; margin-top: 4px; }
.glass-page-head .head-actions { display: flex; gap: 8px; flex-wrap: wrap; }
html[data-theme="light"] .glass-page-head{
  background: rgba(0,0,0,0.02);
  border-color: rgba(0,0,0,0.05);
}

/* Compact variant — used on narrow-wrap pages where the standard height
   would dominate above-the-fold content. */
.glass-page-head.compact { padding: 12px 18px; margin-bottom: 12px; }
.glass-page-head.compact .head-title { font-size: 1.1rem; }

/* ═══════════════════════════════════════════════════════════════════════
   PURCHASE FLOW — components specific to admin/buy.php (and any future
   page that mirrors the same numbered-step + stepper + summary pattern).
   ═══════════════════════════════════════════════════════════════════════ */

/* ───────────────────────── 15. Glass info bar ────────────────────────── */
.glass-info-bar{
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 12px 18px;
  background: rgba(255,255,255,0.04);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 14px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}
.glass-info-bar .info-cell{ display: flex; align-items: center; gap: 8px; font-size: 0.88rem; }
.glass-info-bar .info-cell strong{ font-weight: 700; color: var(--text); }
.glass-info-bar .info-cell.faint{ opacity: 0.7; }
html[data-theme="light"] .glass-info-bar{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.05);
}

/* ───────────────────────── 16. Glass alert ───────────────────────────── */
/* Inline page-level message. Composable with existing utility classes
   like .mt-2/.mb-2. Element can stay hidden via inline display:none or
   a co-class (e.g. .buy-vol-warn from app.css) and be toggled by JS. */
.glass-alert{
  position: relative;
  padding: 12px 16px;
  border-radius: 12px;
  border: 1px solid var(--alert-border, rgba(255,255,255,0.10));
  background: var(--alert-bg, rgba(255,255,255,0.04));
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--text);
  margin-bottom: 12px;
  font-size: 0.9rem;
}
.glass-alert.error,
.glass-alert.danger{
  --alert-bg: rgba(var(--danger-rgb), 0.10);
  --alert-border: rgba(var(--danger-rgb), 0.35);
  color: rgb(248,113,113);
}
.glass-alert.warn{
  --alert-bg: rgba(var(--warn-rgb), 0.10);
  --alert-border: rgba(var(--warn-rgb), 0.35);
  color: rgb(252,211,77);
}
.glass-alert.info{
  --alert-bg: rgba(96,165,250, 0.10);
  --alert-border: rgba(96,165,250, 0.35);
  color: rgb(147,197,253);
}
.glass-alert.success{
  --alert-bg: rgba(var(--success-rgb), 0.10);
  --alert-border: rgba(var(--success-rgb), 0.35);
  color: rgb(74,222,128);
}
html[data-theme="light"] .glass-alert.error,
html[data-theme="light"] .glass-alert.danger  { color: rgb(185, 28, 28); }
html[data-theme="light"] .glass-alert.warn    { color: rgb(146, 64, 14); }
html[data-theme="light"] .glass-alert.info    { color: rgb(29, 78, 216); }
html[data-theme="light"] .glass-alert.success { color: rgb(21, 128, 61); }

/* ───────────────────────── 17. Glass form section ────────────────────── */
.glass-form-section{
  position: relative;
  padding: 20px 22px;
  background: rgba(20,22,32,0.55);
  backdrop-filter: blur(20px) saturate(1.3);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 16px;
  margin-bottom: 14px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.25), inset 0 1px 0 rgba(255,255,255,0.05);
}
.glass-form-section .section-title{
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 14px;
  font-size: 1rem;
  font-weight: 700;
}
/* AUTO-DECISION: numbered cue is a plain inline element rather than a CSS
   counter — counters drift if a section is hidden/reordered, and we want
   the number to be a stable, screen-reader-readable token in the DOM. */
.glass-form-section .section-num{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px; height: 28px;
  border-radius: 50%;
  background: rgba(var(--accent-rgb), 0.15);
  border: 1px solid rgba(var(--accent-rgb), 0.35);
  color: rgb(165,180,252);
  font-weight: 700; font-size: 0.85rem;
}
.glass-form-section .section-title-hint{
  margin-inline-start: auto;
  font-size: 0.78rem;
  font-weight: 400;
  opacity: 0.6;
}
html[data-theme="light"] .glass-form-section{
  background: rgba(255,255,255,0.70);
  border-color: rgba(0,0,0,0.06);
  box-shadow: 0 8px 32px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.5);
}
html[data-theme="light"] .glass-form-section .section-num{ color: rgb(67,56,202); }

/* ───────────────────────── 18. Glass stepper ─────────────────────────── */
.glass-stepper{
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 10px;
  align-items: stretch;
  margin-bottom: 10px;
  width: 100%;
  max-width: 100%;
}
.glass-stepper .stepper-input{
  /* min-width:0 + width:100% lets the grid 1fr track shrink below the
     <input>'s intrinsic ~20ch min-content width — otherwise the stepper
     overflows its container on narrow viewports. */
  min-width: 0;
  width: 100%;
  background: rgba(255,255,255,0.06);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 12px;
  padding: 14px 18px;
  font-size: 1.4rem;
  font-weight: 700;
  text-align: center;
  color: var(--text);
  direction: ltr;
  font-family: inherit;
}
.glass-stepper .stepper-input:focus{
  outline: none;
  border-color: rgba(var(--accent-rgb), 0.5);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.15);
}
/* fixes-merged-02 / Section A: stepper buttons render bare "+" / "−" glyphs
   (no digit). The bare minus specifically reads anemic, so we bump font-size
   noticeably and keep weight at 600 — too heavy a weight makes the glyphs
   look stamped rather than confident. line-height:1 + display:inline-flex
   on .btn-glass already centres the glyph vertically. */
.glass-stepper .btn-glass{
  min-width: 56px;
  font-size: 1.7rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0;
  white-space: nowrap;
}
html[data-theme="light"] .glass-stepper .stepper-input{
  background: rgba(0,0,0,0.03);
  border-color: rgba(0,0,0,0.08);
}
@media (max-width: 480px){
  .glass-stepper{ gap: 6px; }
  .glass-stepper .stepper-input{ padding: 12px 10px; font-size: 1.2rem; }
  /* fixes-merged-02 / Section A: keep the glyph confident at narrow
     widths — the bare "−" reads anemic if it shrinks too aggressively.
     Merged from the original §18 mobile rule and my own override; the
     original's font-size:1rem was the bug (made the glyph 16px). */
  /* UI-0086: also pin a 44px min-height so the glyph buttons keep a full
     WCAG touch target on very narrow phones (≤360px), not just the width. */
  .glass-stepper .btn-glass{ min-width: 44px; min-height: 44px; padding: 10px 12px; font-size: 1.55rem; }
}

/* ─────────────── 18b. Quick-step chips (renew / add-volume modal) ───────
   subscription_detail.php + my_subscription_detail.php render the renew form
   as .stepper-wrap > (.stepper-input-row + .stepper-btns > .stepper-btn).
   These are DISTINCT from the §18 .glass-stepper component above (an inline
   3-column +/− stepper). Without these rules the quick-add chips (-1 -0.5
   +0.5 +1 +5 / -7 +7 +30) fall back to raw browser-default buttons — match
   the glass chip look already used by §19 .dur-btn. JS (initSteppers in
   app.js) keys off [data-stepper] / [data-step], NOT these class names, so
   this is purely cosmetic and never alters the renew behaviour. */
.stepper-wrap{
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.stepper-input-row{ width: 100%; }
.stepper-btns{
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.stepper-btn{
  flex: 1 1 auto;
  min-width: 52px;
  padding: 8px 10px;
  background: rgba(255,255,255,0.04);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 10px;
  color: var(--text);
  font-weight: 600; font-size: 0.85rem;
  font-family: inherit;
  direction: ltr;            /* keep "+5" / "-0.5" glyph order stable in RTL */
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.15s ease;
}
.stepper-btn:hover{
  background: rgba(255,255,255,0.07);
  border-color: rgba(255,255,255,0.14);
  transform: translateY(-1px);
}
.stepper-btn:active{ transform: translateY(0); }
.stepper-btn:disabled{ opacity: 0.45; cursor: not-allowed; transform: none; }
html[data-theme="light"] .stepper-btn{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.06);
}
html[data-theme="light"] .stepper-btn:hover{
  background: rgba(0,0,0,0.05);
  border-color: rgba(0,0,0,0.12);
}
@media (max-width: 480px){
  /* keep a full WCAG touch target on narrow phones */
  .stepper-btn{ min-width: 44px; min-height: 40px; }
}

/* ───────────────────────── 19. Glass duration toggle ─────────────────── */
.glass-duration-toggle{ display: flex; gap: 8px; flex-wrap: wrap; }
.glass-duration-toggle .dur-btn{
  flex: 1 1 calc(33.333% - 6px);
  min-width: 80px;
  padding: 10px 14px;
  background: rgba(255,255,255,0.04);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  color: var(--text);
  font-weight: 600; font-size: 0.9rem;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.15s ease;
}
.glass-duration-toggle .dur-btn:hover{
  background: rgba(255,255,255,0.07);
  border-color: rgba(255,255,255,0.14);
  transform: translateY(-1px);
}
.glass-duration-toggle .dur-btn.active{
  background: rgba(var(--accent-rgb), 0.18);
  border-color: rgba(var(--accent-rgb), 0.5);
  color: rgb(199,210,254);
  box-shadow: 0 0 0 1px rgba(var(--accent-rgb), 0.2), 0 0 20px rgba(var(--accent-rgb), 0.25);
}
html[data-theme="light"] .glass-duration-toggle .dur-btn{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.06);
}
html[data-theme="light"] .glass-duration-toggle .dur-btn:hover{
  background: rgba(0,0,0,0.05);
  border-color: rgba(0,0,0,0.12);
}
html[data-theme="light"] .glass-duration-toggle .dur-btn.active{
  background: rgba(var(--accent-rgb), 0.12);
  color: rgb(67,56,202);
}

/* ───────────────────────── 20. Glass price line ──────────────────────── */
.glass-price-line{
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  margin-top: 10px;
  border-radius: 12px;
  background: rgba(var(--accent-rgb), 0.06);
  border: 1px solid rgba(var(--accent-rgb), 0.18);
  font-size: 1rem;
  font-weight: 600;
  color: var(--text);
  transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
}
.glass-price-line .price-label{ opacity: 0.7; font-weight: 500; font-size: 0.88rem; }
.glass-price-line .price-value{ font-weight: 800; color: rgb(199,210,254); }
/* AUTO-DECISION: .flash class name preserved as the IIFE's toggle target;
   only the visual effect changes (old: opacity dim; new: accent glow). */
.glass-price-line.flash{
  background: rgba(var(--accent-rgb), 0.16);
  border-color: rgba(var(--accent-rgb), 0.45);
  box-shadow: 0 0 24px rgba(var(--accent-rgb), 0.30);
}
html[data-theme="light"] .glass-price-line .price-value{ color: rgb(67,56,202); }

/* ───────────────────────── 21. Glass summary dl ──────────────────────── */
.glass-summary-dl{
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 10px 18px;
  padding: 14px 16px;
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 12px;
  margin-bottom: 14px;
}
.glass-summary-dl dt{ font-size: 0.85rem; opacity: 0.7; align-self: center; }
.glass-summary-dl dd{ margin: 0; text-align: end; font-weight: 600; font-size: 0.95rem; }
.glass-summary-dl dd.total{ font-size: 1.15rem; font-weight: 800; color: rgb(199,210,254); }
html[data-theme="light"] .glass-summary-dl{
  background: rgba(0,0,0,0.02);
  border-color: rgba(0,0,0,0.05);
}
html[data-theme="light"] .glass-summary-dl dd.total{ color: rgb(67,56,202); }

/* ───────────────────────── 22. Glass buy CTA ─────────────────────────── */
.btn-glass.cta-buy{
  width: 100%;
  padding: 16px 24px;
  font-size: 1.05rem;
  font-weight: 800;
  background: linear-gradient(135deg,
    rgba(var(--accent-rgb), 0.18),
    rgba(var(--accent-rgb), 0.08));
  border-color: rgba(var(--accent-rgb), 0.35);
  letter-spacing: 0.3px;
  min-height: 52px;
}
.btn-glass.cta-buy::before{
  inset: -6px;
  filter: blur(28px);
  background: radial-gradient(circle at center, rgba(var(--accent-rgb), 0.7), transparent 70%);
}
.btn-glass.cta-buy:hover{ transform: translateY(-2px); }
.btn-glass.cta-buy:hover::before{ opacity: 1; }
.btn-glass.cta-buy:disabled,
.btn-glass.cta-buy[disabled]{
  /* UI-0065: 0.45 dropped the label below AA, especially in light theme.
     0.6 keeps it clearly "disabled" (no glow, not-allowed cursor, no lift)
     while leaving the text legible. */
  opacity: 0.6;
  cursor: not-allowed;
  transform: none;
}
.btn-glass.cta-buy:disabled::before,
.btn-glass.cta-buy[disabled]::before{ opacity: 0; }

/* ═══════════════════════════════════════════════════════════════════════
   WALLET / DEPOSIT FLOW — financial-UI components for admin/wallet.php.
   ═══════════════════════════════════════════════════════════════════════ */

/* ───────────────────────── 23. Glass balance card ────────────────────── */
.glass-balance-card{
  position: relative;
  text-align: center;
  padding: 36px 24px 28px;
  background: rgba(20,22,32,0.55);
  backdrop-filter: blur(24px) saturate(1.3);
  -webkit-backdrop-filter: blur(24px) saturate(1.3);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 22px;
  margin-bottom: 18px;
  overflow: hidden;
  isolation: isolate;
}
.glass-balance-card::after{
  content:""; position: absolute;
  inset: -40% -10% auto -10%;
  height: 200%;
  background: radial-gradient(circle at 50% 0%, rgba(var(--success-rgb), 0.15), transparent 60%);
  opacity: 0.5;
  z-index: -1;
  pointer-events: none;
}
.glass-balance-card .balance-label{ font-size: 0.88rem; opacity: 0.7; margin-bottom: 6px; }
/* AUTO-DECISION: direction:ltr + unicode-bidi:isolate around the balance
   number so the ASCII digits render in their natural reading order inside
   the RTL flow — without this, large numbers can flip past surrounding
   punctuation. The label/currency siblings are RTL Persian and stay so. */
.glass-balance-card .balance-value{
  font-size: 2.6rem; font-weight: 800;
  letter-spacing: -0.5px;
  line-height: 1.1;
  color: rgb(187,247,208);
  /* UI-0457: a 24px/0.35 glow in the digits' own color haloed the numerals
     and softened the edges. A tighter, fainter glow keeps the accent without
     blurring readability. */
  text-shadow: 0 0 10px rgba(var(--success-rgb), 0.18);
  direction: ltr;
  unicode-bidi: isolate;
  margin-bottom: 4px;
}
.glass-balance-card .balance-currency{
  font-size: 1rem; font-weight: 600; opacity: 0.85;
  margin-inline-start: 6px;
}
.glass-balance-card .balance-reserved{
  margin-top: 14px;
  padding: 8px 14px;
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.82rem;
  background: rgba(var(--warn-rgb), 0.10);
  border: 1px solid rgba(var(--warn-rgb), 0.30);
  border-radius: 999px;
  color: rgb(252,211,77);
}
.glass-balance-card .balance-actions{
  display: flex; justify-content: center; gap: 10px;
  margin-top: 22px;
  flex-wrap: wrap;
}
html[data-theme="light"] .glass-balance-card{
  background: rgba(255,255,255,0.70);
  border-color: rgba(0,0,0,0.06);
}
html[data-theme="light"] .glass-balance-card .balance-value{
  color: rgb(22,101,52);
  text-shadow: 0 0 24px rgba(var(--success-rgb), 0.18);
}
html[data-theme="light"] .glass-balance-card .balance-reserved{
  background: rgba(var(--warn-rgb), 0.08);
  color: rgb(146,64,14);
}

/* H5.3: negative-balance variant + wholesale credit-limit indicator.
   Triggered by `.is-negative` on the balance card (wallet.php sets it
   when Wallet::get()['available'] is < 0). The credit-limit line is
   rendered only when the wallet row carries a non-NULL negative_limit. */
.glass-balance-card.is-negative .balance-value{
  color: rgb(252,165,165);
  /* UI-0457: tighter glow (see positive variant). */
  text-shadow: 0 0 10px rgba(var(--danger-rgb), 0.18);
}
.glass-balance-card.is-negative::after{
  background: radial-gradient(circle at 50% 0%, rgba(var(--danger-rgb), 0.18), transparent 60%);
}
html[data-theme="light"] .glass-balance-card.is-negative .balance-value{
  color: rgb(159,18,57);
  text-shadow: 0 0 24px rgba(var(--danger-rgb), 0.18);
}
.glass-balance-card .balance-credit-limit{
  margin-top: 10px;
  padding: 6px 12px;
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.8rem;
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 999px;
  opacity: 0.9;
}
.glass-balance-card .balance-credit-limit.breached{
  background: rgba(var(--danger-rgb), 0.12);
  border-color: rgba(var(--danger-rgb), 0.35);
  color: rgb(254,202,202);
}
.glass-balance-card .balance-credit-limit .credit-used{
  margin-inline-start: 4px;
  font-weight: 700;
}
html[data-theme="light"] .glass-balance-card .balance-credit-limit{
  background: rgba(0,0,0,0.04);
  border-color: rgba(0,0,0,0.08);
}
html[data-theme="light"] .glass-balance-card .balance-credit-limit.breached{
  background: rgba(var(--danger-rgb), 0.08);
  color: rgb(159,18,57);
}

/* ───────────────────────── 24. Glass select ──────────────────────────── */
.glass-select{
  width: 100%;
  background: rgba(255,255,255,0.05);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 12px;
  padding: 12px 14px;
  color: var(--text);
  font-size: 0.95rem;
  font-weight: 500;
  font-family: inherit;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2' stroke-opacity='0.6'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat;
  background-position: left 14px center; /* RTL: chevron sits on the trailing edge (left) */
  padding-inline-start: 38px;
  cursor: pointer;
}
.glass-select:focus{
  outline: none;
  border-color: rgba(var(--accent-rgb), 0.5);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.15);
}
html[data-theme="light"] .glass-select{
  background-color: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.08);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23000000' stroke-width='2' stroke-opacity='0.5'><polyline points='6 9 12 15 18 9'/></svg>");
}

/* ───────────────────────── 25. Glass method card ─────────────────────── */
.glass-method-card{
  position: relative;
  padding: 16px 18px;
  margin-top: 12px;
  background: rgba(255,255,255,0.04);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 14px;
}
.glass-method-card .method-title{
  display: flex; align-items: center; gap: 8px;
  font-size: 0.88rem; font-weight: 700;
  margin-bottom: 12px;
  color: rgb(199,210,254);
}
.glass-method-card .method-meta{
  margin-top: 10px;
  font-size: 0.82rem; opacity: 0.75;
  display: flex; align-items: center; gap: 6px;
  flex-wrap: wrap;
}
/* AUTO-DECISION: card-method and crypto-method use different border
   accents — bank-card (indigo, our brand accent) vs crypto (amber).
   Visual semantic helps users register WHICH method they picked in
   the moments after clicking, before they've read the label. */
.glass-method-card.card-method{ border-color: rgba(var(--accent-rgb), 0.20); }
.glass-method-card.card-method .method-title{ color: rgb(165,180,252); }
.glass-method-card.crypto-method{ border-color: rgba(245,158,11, 0.25); }
.glass-method-card.crypto-method .method-title{ color: rgb(252,211,77); }

.glass-method-card .card-number{
  font-family: ui-monospace, monospace;
  /* User feedback rev 4: needs to fit cleanly inside a 320-px viewport
     even with the .url-copy-glass padding (10/14) and method-card
     inner padding. Smaller font + tighter letter-spacing keep the
     19-char string ("6104 3378 9992 1010") inside ~230 px so it
     never breaks or overflows on the narrowest mobile target. */
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 1.5px;
  /* Safety — if any narrower context still squeezes us, allow shrink
     before overflow. */
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  direction: ltr;
  unicode-bidi: isolate;
}
.glass-method-card .crypto-address{
  font-family: ui-monospace, monospace;
  font-size: 0.85rem;
  direction: ltr;
  unicode-bidi: isolate;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
html[data-theme="light"] .glass-method-card{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.06);
}
html[data-theme="light"] .glass-method-card.card-method .method-title{ color: rgb(67,56,202); }
html[data-theme="light"] .glass-method-card.crypto-method .method-title{ color: rgb(146,64,14); }

/* ───────────────────────── 26. Glass amount input ────────────────────── */
.glass-amount-input{
  width: 100%;
  background: rgba(255,255,255,0.06);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 14px;
  padding: 16px 20px;
  font-size: 1.5rem;
  font-weight: 700;
  text-align: center;
  color: var(--text);
  font-family: inherit;
  direction: ltr;
  unicode-bidi: isolate;
  letter-spacing: 0.5px;
}
.glass-amount-input::placeholder{
  /* A-H: match input font-size so the first keystroke doesn't cause a
     visible vertical reflow in the wallet top-up modal. Hint contrast
     is carried by opacity + font-weight instead of size. */
  font-size: 1.5rem;
  font-weight: 500;
  opacity: 0.40;
  letter-spacing: 0;
}
.glass-amount-input:focus{
  outline: none;
  border-color: rgba(var(--success-rgb), 0.5);
  box-shadow: 0 0 0 3px rgba(var(--success-rgb), 0.12),
              0 0 30px rgba(var(--success-rgb), 0.20);
}
html[data-theme="light"] .glass-amount-input{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.08);
}

/* ───────────────────────── 27. Glass input group ─────────────────────── */
.glass-input-group{ margin-bottom: 12px; }
.glass-input-group label{
  display: block;
  font-size: 0.85rem; font-weight: 600;
  margin-bottom: 6px;
  opacity: 0.85;
}
.glass-input-group label .faint{
  font-weight: 400; opacity: 0.6;
  margin-inline-start: 4px;
}
.glass-input-group input,
.glass-input-group textarea{
  width: 100%;
  background: rgba(255,255,255,0.04);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 10px;
  padding: 10px 12px;
  color: var(--text);
  font-size: 0.9rem;
  font-family: inherit;
}
.glass-input-group input:focus,
.glass-input-group textarea:focus{
  outline: none;
  border-color: rgba(var(--accent-rgb), 0.45);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.12);
}
html[data-theme="light"] .glass-input-group input,
html[data-theme="light"] .glass-input-group textarea{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.08);
}

/* Nested form section — visually subordinate to a primary .glass-form-section. */
.glass-form-section.nested{
  background: rgba(255,255,255,0.03);
  padding: 14px 16px;
  margin-bottom: 10px;
  border-radius: 12px;
  box-shadow: none;
}
html[data-theme="light"] .glass-form-section.nested{
  background: rgba(0,0,0,0.02);
}

/* ───────────────────────── 28. Glass file upload ─────────────────────── */
/* AUTO-DECISION: <label class="glass-file-upload"> wraps a position:absolute
   invisible <input type="file">. We do NOT use <label for=""> because the
   absolute-inside-label approach keeps the entire drop zone clickable on
   older mobile browsers where label-target click can mis-fire on tap. */
.glass-file-upload{
  position: relative;
  display: flex; align-items: center; gap: 14px;
  padding: 16px 18px;
  background: rgba(255,255,255,0.03);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px dashed rgba(255,255,255,0.18);
  border-radius: 14px;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.glass-file-upload:hover{
  background: rgba(255,255,255,0.06);
  border-color: rgba(var(--accent-rgb), 0.40);
}
.glass-file-upload input[type="file"]{
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  opacity: 0;
  cursor: pointer;
}
.glass-file-upload .file-icon{ font-size: 1.6rem; opacity: 0.7; }
.glass-file-upload .file-info{ flex: 1; }
.glass-file-upload .file-prompt{ font-size: 0.92rem; font-weight: 600; margin-bottom: 2px; }
.glass-file-upload .file-hint  { font-size: 0.78rem; opacity: 0.6; }
.glass-file-upload .file-name  { font-size: 0.82rem; opacity: 0.85; word-break: break-all; margin-top: 2px; }
/* UI-0042: visible drag-over affordance — JS toggles .is-dragging in
   pg_bindDropZone (assets/app.js) but the design system lacked a rule. */
.glass-file-upload.is-dragging{
  border-style: solid;
  border-color: rgba(var(--accent-rgb), 0.60);
  background: rgba(var(--accent-rgb), 0.06);
}
.glass-file-upload.has-file{
  border-style: solid;
  border-color: rgba(var(--success-rgb), 0.40);
  background: rgba(var(--success-rgb), 0.08);
}
.glass-file-upload.has-file .file-icon{ color: rgb(74,222,128); }
html[data-theme="light"] .glass-file-upload{
  background: rgba(0,0,0,0.02);
  border-color: rgba(0,0,0,0.18);
}
html[data-theme="light"] .glass-file-upload:hover{ background: rgba(0,0,0,0.04); }

/* ───────────────────────── 29. Glass textarea ────────────────────────── */
.glass-textarea{
  width: 100%;
  background: rgba(255,255,255,0.04);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  padding: 12px 14px;
  color: var(--text);
  font-size: 0.9rem;
  font-family: inherit;
  resize: vertical;
  min-height: 64px;
}
.glass-textarea:focus{
  outline: none;
  border-color: rgba(var(--accent-rgb), 0.45);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.12);
}
html[data-theme="light"] .glass-textarea{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.08);
}

/* ───────────────────────── 30. Glass deposit CTA ─────────────────────── */
.btn-glass.cta-deposit{
  width: 100%;
  padding: 16px 24px;
  font-size: 1.05rem;
  font-weight: 800;
  background: linear-gradient(135deg,
    rgba(var(--success-rgb), 0.20),
    rgba(var(--success-rgb), 0.08));
  border-color: rgba(var(--success-rgb), 0.40);
  letter-spacing: 0.3px;
  min-height: 52px;
  color: rgb(187,247,208);
}
.btn-glass.cta-deposit::before{
  inset: -6px;
  filter: blur(28px);
  background: radial-gradient(circle at center, rgba(var(--success-rgb), 0.7), transparent 70%);
}
.btn-glass.cta-deposit:hover{ transform: translateY(-2px); }
.btn-glass.cta-deposit:hover::before{ opacity: 1; }
.btn-glass.cta-deposit:disabled,
.btn-glass.cta-deposit[disabled]{
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}
.btn-glass.cta-deposit:disabled::before,
.btn-glass.cta-deposit[disabled]::before{ opacity: 0; }
html[data-theme="light"] .btn-glass.cta-deposit{ color: rgb(20,83,45); }

/* ───────────────────────── 31. Glass transaction table ───────────────── */
/* AUTO-DECISION: kept as <table> rather than glass-row stack — ledger data
   is genuinely tabular (date, type, signed amount, balance-after, note),
   and aligning numeric columns for at-a-glance scanning matters here. */
.glass-tx-table{
  width: 100%;
  border-collapse: collapse;
  background: transparent;
}
.glass-tx-table th{
  text-align: start;
  padding: 10px 12px;
  font-size: 0.78rem; font-weight: 600;
  opacity: 0.6;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  white-space: nowrap;
}
.glass-tx-table td{
  padding: 12px;
  border-bottom: 1px solid rgba(255,255,255,0.04);
  font-size: 0.88rem;
  vertical-align: middle;
}
.glass-tx-table tbody tr{ transition: background 0.2s ease; }
.glass-tx-table tbody tr:hover{ background: rgba(255,255,255,0.03); }
.glass-tx-table tr:last-child td{ border-bottom: none; }
/* UI-0047: tabular figures so the amount / balance columns align cleanly
   row-to-row (Vazirmatn renders proportional digits by default). */
.glass-tx-table td[data-label="مبلغ"],
.glass-tx-table td[data-label="موجودی پس از"]{ font-variant-numeric: tabular-nums; }

.glass-tx-table .tx-amount-positive,
.leaderboard .tx-amount-positive,
.tx-amount-positive{ color: rgb(74,222,128); font-weight: 700; direction: ltr; unicode-bidi: isolate; }
.glass-tx-table .tx-amount-negative,
.leaderboard .tx-amount-negative,
.tx-amount-negative{ color: rgb(248,113,113); font-weight: 700; direction: ltr; unicode-bidi: isolate; }
.glass-tx-table .tx-balance-after  { font-weight: 600; opacity: 0.85; direction: ltr; unicode-bidi: isolate; }
.glass-tx-table .tx-date           { font-size: 0.78rem; opacity: 0.65; direction: ltr; unicode-bidi: isolate; white-space: nowrap; }
/* H5.2: cap note width and force long unbroken strings to wrap so a
   single verbose note can't blow out the column width on desktop or
   inflate one mobile card (the table-to-card-collapse at ≤768px
   inherits these constraints — `display:flex` per cell keeps the
   label aligned). max-width is the desktop ceiling; min-width:0
   lets the cell shrink inside the flex card-collapse row. */
.glass-tx-table .tx-note{
  opacity: 0.75;
  font-size: 0.82rem;
  max-width: 38ch;
  min-width: 0;
  overflow-wrap: anywhere;
  word-break: break-word;
}
@media (max-width: 768px){
  /* Inside the card-collapse the cell is display:flex with the label
     on the leading edge — give the note plenty of room but still cap
     it so a single 400-char note can't render an unbounded card. */
  .glass-tx-table .tx-note{ max-width: 100%; }
}

html[data-theme="light"] .glass-tx-table th{ border-bottom-color: rgba(0,0,0,0.08); }
html[data-theme="light"] .glass-tx-table td{ border-bottom-color: rgba(0,0,0,0.05); }
html[data-theme="light"] .glass-tx-table tbody tr:hover{ background: rgba(0,0,0,0.03); }
html[data-theme="light"] .glass-tx-table .tx-amount-positive,
html[data-theme="light"] .leaderboard .tx-amount-positive,
html[data-theme="light"] .tx-amount-positive{ color: rgb(22,101,52); }
html[data-theme="light"] .glass-tx-table .tx-amount-negative,
html[data-theme="light"] .leaderboard .tx-amount-negative,
html[data-theme="light"] .tx-amount-negative{ color: rgb(185,28,28); }

/* ───────────────────────── 32. Glass tx badge ────────────────────────── */
.glass-tx-badge{
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.75rem; font-weight: 600;
  background: rgba(255,255,255,0.06);
  border: 1px solid var(--tx-badge-border, rgba(255,255,255,0.10));
  color: var(--tx-badge-text, var(--text));
}
.glass-tx-badge.deposit    { --tx-badge-border: rgba(var(--success-rgb), 0.5); --tx-badge-text: rgb(74,222,128); }
.glass-tx-badge.purchase   { --tx-badge-border: rgba(var(--warn-rgb), 0.5);    --tx-badge-text: rgb(252,211,77); }
.glass-tx-badge.refund     { --tx-badge-border: rgba(96,165,250, 0.5);         --tx-badge-text: rgb(147,197,253); }
.glass-tx-badge.adjustment { --tx-badge-border: rgba(156,163,175, 0.4);        --tx-badge-text: rgb(209,213,219); }

html[data-theme="light"] .glass-tx-badge.deposit    { --tx-badge-text: rgb(22,101,52); }
html[data-theme="light"] .glass-tx-badge.purchase   { --tx-badge-text: rgb(146,64,14); }
html[data-theme="light"] .glass-tx-badge.refund     { --tx-badge-text: rgb(29,78,216); }
html[data-theme="light"] .glass-tx-badge.adjustment { --tx-badge-text: rgb(75,85,99); }

/* ───────────────────────── 33. Glass deposit row ─────────────────────── */
.glass-deposit-row{
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 12px;
  align-items: center;
  padding: 12px 16px;
  background: rgba(255,255,255,0.03);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 12px;
  margin-bottom: 8px;
}
.glass-deposit-row .dep-amount{
  font-size: 1rem;
  font-weight: 700;
  direction: ltr;
  unicode-bidi: isolate;
}
.glass-deposit-row .dep-meta{
  font-size: 0.78rem;
  opacity: 0.6;
  margin-top: 2px;
}
.glass-deposit-row .dep-actions{
  display: flex; gap: 6px; align-items: center;
}
html[data-theme="light"] .glass-deposit-row{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.05);
}

/* ═══════════════════════════════════════════════════════════════════════
   REPRESENTATIVES MANAGEMENT — tier badges, margin cells, actions menu,
   and the form-row helper used by the create-rep + create-child modals.
   ═══════════════════════════════════════════════════════════════════════ */

/* ───────────────────────── 34. Glass tier badge ──────────────────────── */
/* AUTO-DECISION: indigo / blue / purple = depth gradient. L1 sits closest
   to sudo (brand accent indigo), L2 mid (blue), L3 deepest (purple). The
   colors are subtle but scannable: a column of mixed-tier rows reads as
   a visible depth map at a glance. */
.glass-tier-badge{
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 38px;
  padding: 3px 10px;
  border-radius: 8px;
  font-size: 0.78rem; font-weight: 700;
  background: rgba(255,255,255,0.06);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--tier-border-color, rgba(255,255,255,0.10));
  color: var(--tier-text-color, var(--text));
  direction: ltr;
  unicode-bidi: isolate;
  letter-spacing: 0.5px;
}
.glass-tier-badge.l1{
  --tier-border-color: rgba(var(--accent-rgb), 0.45);
  --tier-text-color: rgb(199,210,254);
  background: rgba(var(--accent-rgb), 0.10);
}
.glass-tier-badge.l2{
  --tier-border-color: rgba(96,165,250, 0.45);
  --tier-text-color: rgb(147,197,253);
  background: rgba(96,165,250, 0.10);
}
.glass-tier-badge.l3{
  --tier-border-color: rgba(168,85,247, 0.45);
  --tier-text-color: rgb(216,180,254);
  background: rgba(168,85,247, 0.10);
}
html[data-theme="light"] .glass-tier-badge.l1{ --tier-text-color: rgb(67,56,202); }
html[data-theme="light"] .glass-tier-badge.l2{ --tier-text-color: rgb(29,78,216); }
html[data-theme="light"] .glass-tier-badge.l3{ --tier-text-color: rgb(126,34,206); }

/* ───────────────────────── 35. Glass margin cell ─────────────────────── */
/* AUTO-DECISION: negative margins get a stronger glow than positive ones.
   Healthy margins should fade into the data; anomalies (Q9 inversion
   surface) should grab attention. */
.glass-margin-cell{
  display: inline-flex; align-items: center; gap: 4px;
  font-weight: 700;
  direction: ltr; unicode-bidi: isolate;
  padding: 2px 8px;
  border-radius: 6px;
  font-size: 0.88rem;
  transition: box-shadow 0.3s ease;
}
.glass-margin-cell.positive{
  color: rgb(74,222,128);
  background: rgba(var(--success-rgb), 0.08);
  box-shadow: 0 0 12px rgba(var(--success-rgb), 0.12);
}
.glass-margin-cell.negative{
  color: rgb(248,113,113);
  background: rgba(var(--danger-rgb), 0.10);
  box-shadow: 0 0 14px rgba(var(--danger-rgb), 0.20);
}
html[data-theme="light"] .glass-margin-cell.positive{
  color: rgb(22,101,52);
  background: rgba(var(--success-rgb), 0.10);
}
html[data-theme="light"] .glass-margin-cell.negative{
  color: rgb(185,28,28);
  background: rgba(var(--danger-rgb), 0.12);
}
/* UI-0250: a break-even margin (0) is neither profit nor loss — render it
   muted instead of reusing the red "negative" treatment. */
.glass-margin-cell.neutral{
  color: var(--text-d);
  background: rgba(127,127,127, 0.10);
}

/* ───────────────────────── 36. Glass actions menu — REMOVED ──────────
   RULE-7 (Phase 6): the .glass-actions-* dropdown rules (~95 lines) were
   removed along with the legacy .actions-* rules in app.css. The
   replacement is the .row-actions / .btn-glass.icon-only flexbox added
   at the bottom of this file. No markup references the dropdown classes
   anymore (verified via grep).
   ────────────────────────────────────────────────────────────────── */

/* ───────────────────────── 37. Glass form row ────────────────────────── */
.glass-form-row{
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
@media (max-width: 600px) {
  .glass-form-row { grid-template-columns: 1fr; }
}

/* ───────────────────────── 38. Glass input (standalone) ──────────────── */
/* Same input chrome as .glass-input-group input but usable outside a
   .glass-input-group container — handy in the filters bar where the
   search field doesn't need its own label wrapper. */
.glass-input{
  width: 100%;
  background: rgba(255,255,255,0.04);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 10px;
  padding: 10px 12px;
  color: var(--text);
  font-size: 0.9rem;
  font-family: inherit;
}
.glass-input:focus{
  outline: none;
  border-color: rgba(var(--accent-rgb), 0.45);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.12);
}
html[data-theme="light"] .glass-input{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.08);
}

/* ═══════════════════════════════════════════════════════════════════════
   AUTH / LOGIN — centered entrance card + page-level atmosphere.

   AUTO-DECISION: cooperation Option B with app.css's --auth-* token block
   (defined at app.css:52-64 dark / :114-126 light). These classes consume
   --auth-bg, --auth-blob1/2/3, --auth-card, --auth-card-border,
   --auth-card-glow rather than hardcoded RGBs — theme switching continues
   to flow through the existing token toggle.

   AUTO-DECISION: .glass-auth-page and .glass-auth-brand are defined here
   for design-system completeness but NOT applied to the current login.php
   — that page's existing .login-wrap::before already paints the radial-
   blob atmosphere using the same --auth-* tokens. Applying the new
   classes on top would double-paint the gradients. Use these for future
   auth pages that don't have an existing wrapper background.
   ═══════════════════════════════════════════════════════════════════════ */

/* ───────────────────────── 39. Glass auth page ───────────────────────── */
.glass-auth-page{
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 16px;
  position: relative;
  overflow-x: hidden;
  background: var(--auth-bg, var(--bg));
}
.glass-auth-page::before{
  content: "";
  position: fixed;
  inset: 0;
  background:
    radial-gradient(ellipse 65% 55% at 18% 38%, var(--auth-blob1, rgba(var(--accent-rgb), 0.12)) 0%, transparent 70%),
    radial-gradient(ellipse 55% 45% at 82% 22%, var(--auth-blob2, rgba(168,85,247, 0.10)) 0%, transparent 70%),
    radial-gradient(ellipse 40% 35% at 50% 88%, var(--auth-blob3, rgba(96,165,250, 0.08)) 0%, transparent 70%);
  pointer-events: none;
  z-index: 0;
}
/* AUTO-DECISION: dot-grid uses ::after (not ::before, which carries the
   radial blobs) — keeps both layers compositable. 32px spacing + 4% alpha
   gives texture without noise; tested by eye in both themes. */
.glass-auth-page::after{
  content: "";
  position: fixed;
  inset: 0;
  background-image:
    radial-gradient(circle at 1px 1px, rgba(255,255,255,0.04) 1px, transparent 0);
  background-size: 32px 32px;
  pointer-events: none;
  z-index: 0;
  opacity: 0.6;
}
html[data-theme="light"] .glass-auth-page::after{
  background-image:
    radial-gradient(circle at 1px 1px, rgba(0,0,0,0.04) 1px, transparent 0);
}

/* ───────────────────────── 40. Glass auth brand ──────────────────────── */
/* Optional micro-pattern for pages that don't already have a brand block. */
.glass-auth-brand{
  text-align: center;
  margin-bottom: 24px;
  position: relative;
  z-index: 1;
}
.glass-auth-brand .brand-mark{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px; height: 64px;
  margin-bottom: 12px;
  border-radius: 18px;
  background: rgba(var(--accent-rgb), 0.12);
  border: 1px solid rgba(var(--accent-rgb), 0.30);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 0 32px rgba(var(--accent-rgb), 0.25);
}
.glass-auth-brand .brand-name{
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: 0.5px;
  color: var(--text);
}
.glass-auth-brand .brand-tagline{
  font-size: 0.85rem;
  opacity: 0.65;
  margin-top: 4px;
}

/* ───────────────────────── 41. Glass auth card ───────────────────────── */
/* AUTO-DECISION: co-classed onto .login-card; uses --auth-card / -border /
   -glow tokens so theme switching flows through. The visual upgrade
   layered on top is the outer accent halo (::after radial glow) — the
   detail that gives an auth surface presence without being loud. */
.glass-auth-card{
  position: relative;
  width: 100%;
  max-width: 440px;
  padding: 32px 28px;
  background: var(--auth-card, rgba(20,22,32,0.65));
  backdrop-filter: blur(28px) saturate(1.4);
  -webkit-backdrop-filter: blur(28px) saturate(1.4);
  border: 1px solid var(--auth-card-border, rgba(255,255,255,0.10));
  border-radius: 22px;
  box-shadow: var(--auth-card-glow, 0 20px 60px rgba(0,0,0,0.45)),
              inset 0 1px 0 rgba(255,255,255,0.08);
  z-index: 1;
}
.glass-auth-card::after{
  content: "";
  position: absolute;
  inset: -40px;
  background: radial-gradient(circle at center, rgba(var(--accent-rgb), 0.18), transparent 60%);
  filter: blur(40px);
  z-index: -1;
  pointer-events: none;
}
.glass-auth-card .auth-title{
  font-size: 1.4rem;
  font-weight: 800;
  margin-bottom: 6px;
  text-align: center;
}
.glass-auth-card .auth-subtitle{
  font-size: 0.88rem;
  opacity: 0.65;
  text-align: center;
  margin-bottom: 22px;
}
.glass-auth-card .auth-form > * + *{ margin-top: 14px; }
.glass-auth-card .auth-footer{
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px solid rgba(255,255,255,0.06);
  text-align: center;
  font-size: 0.82rem;
  opacity: 0.6;
}
html[data-theme="light"] .glass-auth-card::after{
  background: radial-gradient(circle at center, rgba(var(--accent-rgb), 0.08), transparent 60%);
}
html[data-theme="light"] .glass-auth-card .auth-footer{
  border-top-color: rgba(0,0,0,0.06);
}

/* ───────────────────────── 42. Auth submit CTA ───────────────────────── */
/* AUTO-DECISION: .auth-cta is a smaller-padding variant of .cta-buy/.cta-deposit.
   Login isn't a financial CTA — full cta-buy padding (16px 24px, 52px min-height)
   would overweight the auth surface. 14px 20px / 48px min-height matches the
   visual rhythm of the form's inputs. */
.btn-glass.auth-cta{
  width: 100%;
  padding: 14px 20px;
  font-size: 1rem;
  font-weight: 700;
  background: linear-gradient(135deg,
    rgba(var(--accent-rgb), 0.18),
    rgba(var(--accent-rgb), 0.08));
  border-color: rgba(var(--accent-rgb), 0.35);
  min-height: 48px;
  letter-spacing: 0.3px;
}
.btn-glass.auth-cta::before{
  inset: -4px;
  filter: blur(22px);
  background: radial-gradient(circle at center, rgba(var(--accent-rgb), 0.6), transparent 70%);
}
.btn-glass.auth-cta:hover{ transform: translateY(-1px); }
.btn-glass.auth-cta:hover::before{ opacity: 0.9; }
.btn-glass.auth-cta:disabled,
.btn-glass.auth-cta[disabled]{
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}
.btn-glass.auth-cta:disabled::before,
.btn-glass.auth-cta[disabled]::before{ opacity: 0; }

/* ═══════════════════════════════════════════════════════════════════════
   MOBILE BOTTOM NAVIGATION — fixed glass bar visible at ≤ 768px.
   Replaces the legacy .mobile-nav / .mbn-* block in app.css:571-603.

   AUTO-DECISION: height uses the existing --mob-nav-h token (68px). The
   universal body padding rule at app.css:146 already accounts for this
   token; matching it keeps the bottom padding correct without touching
   any page-level markup.

   AUTO-DECISION: body padding strategy is opt-out (mirrors existing
   `body.auth-page { padding-bottom: 0 }` at app.css:150) rather than the
   brief's opt-in `body.has-bottom-nav`. Touching every page that
   includes nav.php (8+ pages) to add a class would be churn for zero
   functional gain — the universal rule already exists.
   ═══════════════════════════════════════════════════════════════════════ */

/* ───────────────────────── 43. Bottom-nav surface ────────────────────── */
.glass-bottom-nav{
  position: fixed;
  inset: auto 0 0 0;
  display: none; /* shown only on mobile via the media query below */
  z-index: 150;  /* matches legacy .mobile-nav z-index */
  background: rgba(20,22,32,0.75);
  backdrop-filter: blur(24px) saturate(1.5);
  -webkit-backdrop-filter: blur(24px) saturate(1.5);
  border-top: 1px solid rgba(255,255,255,0.08);
  box-shadow: 0 -8px 32px rgba(0,0,0,0.25);
  padding-bottom: env(safe-area-inset-bottom, 0);
}

/* AUTO-DECISION: `display: none` outside the breakpoint rather than
   `visibility: hidden` — visibility:hidden still claims layout space at
   the bottom of every desktop page; display:none drops it cleanly. */
@media (max-width: 768px) {
  .glass-bottom-nav { display: grid; }
}

html[data-theme="light"] .glass-bottom-nav{
  background: rgba(255,255,255,0.80);
  border-top-color: rgba(0,0,0,0.06);
  box-shadow: 0 -8px 32px rgba(0,0,0,0.08);
}

/* AUTO-DECISION: extend the existing universal `body { padding-bottom:
   var(--mob-nav-h) }` (app.css:146) with safe-area-inset on mobile so the
   iOS home-indicator area gets glass background and bottom content gets
   breathing room. Auth pages opt out via the existing `body.auth-page`
   rule at app.css:150. */
@media (max-width: 768px) {
  body:not(.auth-page) {
    padding-bottom: calc(var(--mob-nav-h, 68px) + env(safe-area-inset-bottom, 0) + 8px);
  }
}

/* ───────────────────────── 44. Nav items layout ──────────────────────── */
/* AUTO-DECISION: grid-auto-flow:column + grid-auto-columns:1fr distributes
   N items equally without needing to know N. Flex space-around with
   `flex: 1` does the same; grid is one declaration shorter and aligns
   with the rest of the design system's grid usage. */
.glass-bottom-nav .nav-items{
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  height: var(--mob-nav-h, 68px);
}

.glass-nav-item{
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  text-decoration: none;
  color: rgba(255,255,255,0.55);
  font-size: 0.7rem;
  font-weight: 600;
  padding: 6px 4px;
  transition: color 0.2s ease, transform 0.12s ease, opacity 0.12s ease;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.glass-nav-item:active{
  transform: scale(0.96);
  opacity: 0.85;
}
.glass-nav-item .nav-icon{
  font-size: 22px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  z-index: 1;
}
.glass-nav-item .nav-label{
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 64px;
}
html[data-theme="light"] .glass-nav-item{
  color: rgba(0,0,0,0.55);
}

/* ───────────────────────── 45. Active state — over-determined ────────── */
/* AUTO-DECISION: active state is over-determined (color + radial glow +
   top pill). Mobile bottom-nav must be glanceable in a fraction of a
   second at arm's length; any single signal is fragile, three composing
   signals are robust. */
.glass-nav-item.active{ color: rgb(165,180,252); }
.glass-nav-item.active .nav-icon{ color: rgb(165,180,252); }

/* Radial backlight behind the active icon. */
.glass-nav-item.active .nav-icon::before{
  content: "";
  position: absolute;
  inset: -10px;
  background: radial-gradient(circle at center, rgba(var(--accent-rgb), 0.35), transparent 60%);
  filter: blur(12px);
  z-index: -1;
  pointer-events: none;
}

/* AUTO-DECISION: top-edge pill animates IN on every page load (forwards
   fill mode). The user perceives navigation as a discrete jump from page
   to page; an animation each time reinforces that the new page has
   "received" them. CSS-only — no JS sequencing required. */
.glass-nav-item.active::before{
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%) scaleX(0);
  width: 32px;
  height: 3px;
  border-radius: 0 0 4px 4px;
  background: rgb(var(--accent-rgb));
  box-shadow: 0 0 8px rgba(var(--accent-rgb), 0.6);
  animation: nav-pill-in 220ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
@keyframes nav-pill-in {
  0%   { transform: translateX(-50%) scaleX(0);    opacity: 0; }
  60%  { transform: translateX(-50%) scaleX(1.08); opacity: 1; }
  100% { transform: translateX(-50%) scaleX(1);    opacity: 1; }
}

html[data-theme="light"] .glass-nav-item.active        { color: rgb(67,56,202); }
html[data-theme="light"] .glass-nav-item.active .nav-icon{ color: rgb(67,56,202); }
html[data-theme="light"] .glass-nav-item.active .nav-icon::before{
  background: radial-gradient(circle at center, rgba(var(--accent-rgb), 0.20), transparent 60%);
}

/* ───────────────────────── 46. Notification count badge ──────────────── */
/* Carried over from legacy .mbn-badge — same red dot with count, just
   re-styled to match the glass aesthetic. */
/* H1.14: anchor the badge with logical positioning so the placement
   flips correctly with locale and never overlaps the icon on narrow
   (sub-72px) nav cells. The cell already establishes position:
   relative; inset-inline-end picks the trailing edge in either
   reading direction. The offset is tuned so the badge hugs the
   icon's upper-trailing corner without crossing the cell center —
   width-agnostic, so it stays readable on 50–96 px cells. */
.glass-nav-badge{
  position: absolute;
  top: 2px;
  inset-inline-end: calc(50% - 18px);
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: linear-gradient(180deg,
    rgba(var(--danger-rgb), 0.95),
    rgba(var(--danger-rgb), 0.78));
  color: #fff;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.2px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 2px rgba(20,22,32,0.85),
              0 2px 6px rgba(var(--danger-rgb), 0.35);
  z-index: 2;
}
html[data-theme="light"] .glass-nav-badge{
  box-shadow: 0 0 0 2px rgba(255,255,255,0.92),
              0 2px 6px rgba(var(--danger-rgb), 0.25);
}

/* ══════════════════════════════════════════════════════════════════
   RULE-7 (Phase 6) — Inline row actions (replaces the ⋯ dropdown).
   ══════════════════════════════════════════════════════════════════
   The .row-actions container flows the per-row action buttons
   horizontally in the same <td> as the row content. Each action is
   a .btn-glass.icon-only (emoji-only, with title=/aria-label=).
   .row-actions-cell is applied to the parent <td> so Rule-8 mobile
   CSS can target it for the card-collapsed layout. */
.row-actions{
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
}
.row-action-form{
  /* Each action that needs to POST is wrapped in its own <form>; this
     keeps the form display inline so it doesn't break the flex row. */
  display: inline-flex;
  margin: 0;
}
.btn-glass.icon-only{
  /* AUTO-DECISION (Phase 6 §8 #11): padding + content-box + min-width/height
     trick keeps the visual size small (28×28 desktop, 36×36 mobile) while
     expanding the actual click target to 44×44 — WCAG mobile touch-target. */
  padding: 4px;
  box-sizing: content-box;
  width: 28px;
  height: 28px;
  min-width: 44px;
  min-height: 44px;
  font-size: 0.95rem;
  line-height: 1;
  /* Reset rounded-pill from base .btn-glass — square-ish chip looks
     better when multiple icon buttons sit side-by-side. */
  border-radius: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
@media (max-width: 768px){
  .btn-glass.icon-only{
    width: 36px;
    height: 36px;
    font-size: 1.05rem;
  }
}
/* UI-0017: on a fine pointer (desktop mouse) the 44px touch-target minimum is
   not required (WCAG 2.5.8 allows 24px for pointer input). Relax the icon
   buttons there so a long action row (up to ~9 icons) stops forcing the rep
   table into horizontal scroll on 1366px laptops. Coarse-pointer / mobile
   keeps the 44px target above. The .row-actions container already wraps. */
@media (pointer: fine) and (min-width: 769px){
  .btn-glass.icon-only{
    min-width: 32px;
    min-height: 32px;
  }
}

/* UI-0036: when the buy page can't be afforded, the pay CTA is disabled and
   demoted to a muted treatment so the (actionable) recharge link reads as the
   primary action instead of two competing primaries. */
.btn-glass.cta-buy.cta-muted{
  background: rgba(255,255,255,0.05);
  border-color: rgba(255,255,255,0.12);
  box-shadow: none;
  opacity: 0.55;
}
.btn-glass.cta-buy.cta-muted::before{ opacity: 0 !important; }
html[data-theme="light"] .btn-glass.cta-buy.cta-muted{
  background: rgba(0,0,0,0.04);
  border-color: rgba(0,0,0,0.10);
}

/* ══════════════════════════════════════════════════════════════════
   RULE-8 (Phase 6) — Mobile-first responsive overhaul
   ══════════════════════════════════════════════════════════════════
   Breakpoints (consistent across all CSS additions in this rule):
     ≥ 1024px  desktop      tables full-width
     768–1023  tablet       tables scroll inside .table-wrap (existing)
     < 768     phone        tables card-collapse, modals slide from bottom

   Every <td> in body rows must carry data-label="…" matching its column
   header for the card-collapse to render readable rows. Pages touched in
   this rule:
     admin/representatives.php
     admin/dashboard.php  (leaderboard)
     admin/wallet.php     (transactions list)
     admin/deposits.php
     admin/transactions.php
     admin/transfer.php
     admin/queue.php
     admin/subscriptions.php  (already a vertical row-list — verify)
     admin/my_subscriptions.php
*/

/* ─── Table → card collapse on phone ─────────────────────────────── */
/* Selector list covers every table flavor in admin/:
     .glass-tx-table              — representatives, wallet
     .leaderboard                 — dashboard (sudo + rep variants)
     .table-wrap > table          — deposits (bare <table>)
     .tbl-wrap > table            — transactions, queue, transfer, my_subscriptions
   To opt OUT of card-collapse, add class="no-collapse" to the <table>. */
@media (max-width: 768px){
  .glass-tx-table thead,
  .leaderboard thead,
  .table-wrap > table:not(.no-collapse) thead,
  .tbl-wrap > table:not(.no-collapse) thead{ display: none; }

  .glass-tx-table,
  .glass-tx-table tbody,
  .glass-tx-table tr,
  .glass-tx-table td,
  .leaderboard,
  .leaderboard tbody,
  .leaderboard tr,
  .leaderboard td,
  .table-wrap > table:not(.no-collapse),
  .table-wrap > table:not(.no-collapse) tbody,
  .table-wrap > table:not(.no-collapse) tr,
  .table-wrap > table:not(.no-collapse) td,
  .tbl-wrap > table:not(.no-collapse),
  .tbl-wrap > table:not(.no-collapse) tbody,
  .tbl-wrap > table:not(.no-collapse) tr,
  .tbl-wrap > table:not(.no-collapse) td{
    display: block; width: 100%;
  }

  .glass-tx-table tr,
  .leaderboard tr,
  .table-wrap > table:not(.no-collapse) tr,
  .tbl-wrap > table:not(.no-collapse) tr{
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 14px;
    padding: 12px;
    margin-bottom: 12px;
    background: rgba(20,22,32,0.45);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
  }

  .glass-tx-table td,
  .leaderboard td,
  .table-wrap > table:not(.no-collapse) td,
  .tbl-wrap > table:not(.no-collapse) td{
    border: none;
    padding: 6px 0;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
  }
  .glass-tx-table td::before,
  .leaderboard td::before,
  .table-wrap > table:not(.no-collapse) td::before,
  .tbl-wrap > table:not(.no-collapse) td::before{
    content: attr(data-label);
    font-size: 0.8rem;
    opacity: 0.65;
    flex-shrink: 0;
  }
  /* .row-actions-cell (Rule 7): sit at the bottom of the stacked card,
     separated by a hairline divider. No label prefix; buttons go full
     width on the trailing edge. */
  .glass-tx-table .row-actions-cell,
  .leaderboard .row-actions-cell{
    padding-top: 10px;
    border-top: 1px solid rgba(255,255,255,0.06);
    margin-top: 8px;
  }
  .glass-tx-table .row-actions-cell::before,
  .leaderboard .row-actions-cell::before{ display: none; }
  .glass-tx-table .row-actions,
  .leaderboard .row-actions{ width: 100%; justify-content: flex-end; }
}
html[data-theme="light"] .glass-tx-table tr,
html[data-theme="light"] .leaderboard tr,
html[data-theme="light"] .table-wrap > table tr,
html[data-theme="light"] .tbl-wrap > table tr{
  background: rgba(255,255,255,0.65);
}

/* ─── Modal: bottom-sheet on phone ────────────────────────────────── */
/* fixes-merged-02 / Issue 1: bottom-sheet uses 100dvh to honor the mobile
   URL bar, and pins padding-bottom to safe-area-inset so the iOS home
   indicator doesn't cover the CTA. The flex column inherited from §9
   ensures the body scroll happens INSIDE the box. */
@media (max-width: 768px){
  .glass-modal{ align-items: flex-end; }
  .glass-modal .modal-box{
    border-radius: 18px 18px 0 0;
    /* A-M: 92vh fallback before 92dvh for iOS Safari <15.4. */
    max-height: 92vh;
    max-height: 92dvh;
    margin-top: auto;
    width: 100%;
    max-width: 100%;
    /* H4.12: mirror the bottom safe-area padding on the top edge so
       the iOS URL bar / notch can't overlap the modal header. The
       12px baseline matches the existing modal-header offset on
       phone-sized layouts; safe-area-inset-top stays 0 on browsers
       without the notch, so desktop / Android-without-cutout are
       unaffected. */
    padding-top: max(12px, env(safe-area-inset-top, 12px));
    padding-bottom: max(16px, env(safe-area-inset-bottom, 16px));
  }
}

/* ─── Desktop top header: hide on phone (bottom nav owns navigation) ─ */
@media (max-width: 768px){
  .header{ display: none; }
}

/* ─── Sticky CTA: buy + wallet topup submit buttons ─────────────────
   H4.1: bump `bottom` so the CTA clears the bottom-nav (var(--mob-nav-h)
   = 68px) on mobile, and raise z-index above the nav (which sits at
   150). Without this the primary buy/deposit button is hidden under
   the nav strip on phones. */
@media (max-width: 768px){
  .btn-glass.cta-buy,
  .btn-glass.cta-deposit{
    position: sticky;
    bottom: calc(var(--mob-nav-h, 68px) + 12px + env(safe-area-inset-bottom, 0px));
    z-index: 160;
  }
}

/* ──────────────────────────────────────────────────────────────────
   Theme toggle FAB (dashboard-only — rendered by partials/nav.php
   when $cur === 'dashboard.php'). Fixed positioning so it survives
   the ≤768px breakpoint that hides .header.
   ────────────────────────────────────────────────────────────────── */
.theme-fab{
  position: fixed;
  top: 14px;
  left: 14px;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  /* A-K: use canonical existing design-system tokens (app.css declares
     --border + --surface-2 in both themes) instead of the previously
     referenced --glass-border / --glass-bg which were never defined. */
  border: 1px solid var(--border, rgba(255,255,255,0.18));
  background: var(--surface-2, rgba(28,32,52,0.75));
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: inherit;
  font-size: 1.1rem;
  line-height: 1;
  padding: 0;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(0,0,0,0.18);
  z-index: 1100;
  transition: transform .15s ease, box-shadow .15s ease, background .15s ease;
}
.theme-fab:hover{
  transform: scale(1.06);
  box-shadow: 0 6px 18px rgba(0,0,0,0.24);
}
.theme-fab:active{ transform: scale(0.96); }
.theme-fab:focus-visible{
  outline: 2px solid var(--accent, #8b9dff);
  outline-offset: 2px;
}
html[data-theme="light"] .theme-fab{
  background: rgba(255,255,255,0.85);
  border-color: rgba(0,0,0,0.08);
}
@media (max-width: 768px){
  .theme-fab{ top: 10px; left: 10px; width: 38px; height: 38px; font-size: 1rem; }
}

/* ──────────────────────────────────────────────────────────────────
   App background (post-login only — class is added by partials/nav.php,
   which is included exclusively by authenticated pages). The per-theme
   `background-image: linear-gradient(overlay), url(photo)` declarations
   are emitted inline by nav.php so they reflect the current sudo config.
   These static rules cover the rest: cover-sizing, center, no-repeat,
   and fixed attachment for a parallax-feel that survives scroll.

   Both background-size and background-position list two layers because
   the inline emission stacks overlay + image — same count on each axis.
   ────────────────────────────────────────────────────────────────── */
body.has-app-bg{
  background-size: cover, cover;
  background-position: center center, center center;
  background-repeat: no-repeat, no-repeat;
  background-attachment: fixed, fixed;
}
/* iOS Safari < 15 ignores background-attachment:fixed on body when
   touch-scrolling. Fall back to local attachment on mobile so the image
   still renders (it'll scroll with content — acceptable trade-off vs.
   blank background). */
@media (max-width: 768px){
  body.has-app-bg{ background-attachment: local, local; }
}

/* ──────────────────────────────────────────────────────────────────
   Preset gallery (admin/settings.php appearance card)
   ────────────────────────────────────────────────────────────────── */
.bg-preset-grid{
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px;
  margin-top: 6px;
}
.bg-preset-tile{
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: flex-start;
  aspect-ratio: 16 / 10;
  border-radius: 12px;
  border: 2px solid rgba(255,255,255,0.10);
  background-size: cover;
  background-position: center;
  cursor: pointer;
  overflow: hidden;
  transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.bg-preset-tile:hover{
  transform: translateY(-2px);
  border-color: rgba(var(--accent-rgb, 139,157,255), 0.5);
  box-shadow: 0 6px 18px rgba(0,0,0,0.25);
}
.bg-preset-tile.selected{
  border-color: var(--accent, #8b9dff);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 139,157,255), 0.25);
}
.bg-preset-tile input[type="radio"]{
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.bg-preset-label{
  padding: 4px 8px;
  margin: 8px;
  font-size: 0.78rem;
  font-weight: 600;
  background: rgba(0,0,0,0.55);
  color: #fff;
  border-radius: 6px;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.bg-preset-check{
  position: absolute;
  top: 6px;
  inset-inline-end: 6px;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.85rem;
  border-radius: 50%;
  background: var(--accent, #8b9dff);
  color: #fff;
  box-shadow: 0 2px 6px rgba(0,0,0,0.3);
}
html[data-theme="light"] .bg-preset-tile{ border-color: rgba(0,0,0,0.08); }
@media (max-width: 480px){
  .bg-preset-grid{ grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 8px; }
}


/* ═══════════════════════════════════════════════════════════════════════
   §47  REPRESENTATIVES MOBILE CARD — fixes-merged-02 / Issue 2 (Stage 1)
   ═══════════════════════════════════════════════════════════════════════
   Card layout that replaces the table at ≤ 768 px. Desktop ≥ 769 px keeps
   the existing .glass-tx-table. Two CSS hooks the patch adds on the PHP
   side:
     - .glass-rep-card-list   wrapper around the foreach
     - .rep-list-table        added to the existing <div class="table-wrap">
   This file owns the visibility swap; the PHP file owns the markup.
   ═══════════════════════════════════════════════════════════════════════ */

.glass-rep-card-list{ display: none; }
@media (max-width: 768px){
  .glass-rep-card-list{
    display: grid;
    grid-template-columns: 1fr;
    gap: 12px;
    padding: 4px 0;
  }
  /* Hide the desktop table on phone. The .glass-tx-table card-collapse
     rules from RULE-8 above would otherwise render a SECOND copy of the
     listing alongside the new cards. */
  .glass-card > .table-wrap.rep-list-table{ display: none; }
}

.glass-rep-card{
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px;
  background: rgba(20,22,32,0.55);
  backdrop-filter: blur(18px) saturate(1.3);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 18px;
  box-shadow: 0 6px 24px rgba(0,0,0,0.30), inset 0 1px 0 rgba(255,255,255,0.05);
  /* Overflow visible so the <details class="rep-menu"> panel can render
     outside the card when the portal append is NOT loaded. */
  overflow: visible;
}
html[data-theme="light"] .glass-rep-card{
  background: rgba(255,255,255,0.70);
  border-color: rgba(0,0,0,0.06);
  box-shadow: 0 6px 24px rgba(0,0,0,0.08), inset 0 1px 0 rgba(255,255,255,0.5);
}

/* Header row */
.rep-card-head{
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) auto;
  gap: 12px;
  align-items: center;
}
.rep-card-avatar{
  width: 40px; height: 40px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-weight: 800;
  font-size: 1.05rem;
  letter-spacing: 0;
  box-shadow: 0 2px 8px rgba(0,0,0,0.25), inset 0 0 0 1px rgba(255,255,255,0.10);
  /* Slight inner glow so darker palette colors still pop on dark bg. */
  filter: saturate(1.05);
}
.rep-card-identity{
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.rep-card-name{
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-weight: 700;
  font-size: 0.98rem;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rep-card-name .rep-card-id{
  font-family: ui-monospace, monospace;
  opacity: 0.55;
  font-weight: 600;
  font-size: 0.78rem;
}
.rep-card-username{
  font-family: ui-monospace, monospace;
  font-size: 0.78rem;
  opacity: 0.65;
  direction: ltr;
  unicode-bidi: isolate;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rep-card-flags{
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  flex-shrink: 0;
}
.rep-card-flags .badge-glass,
.rep-card-flags .glass-tier-badge{ font-size: 0.72rem; padding: 3px 9px; }

/* Metrics row */
.rep-card-metrics{
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  padding: 12px;
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 12px;
}
html[data-theme="light"] .rep-card-metrics{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.05);
}
.rep-card-metric{
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-items: flex-end;     /* RTL: numbers/labels read right-aligned */
  padding: 0 8px;
}
.rep-card-metric + .rep-card-metric{
  border-inline-start: 1px solid rgba(255,255,255,0.06);
}
html[data-theme="light"] .rep-card-metric + .rep-card-metric{
  border-inline-start-color: rgba(0,0,0,0.06);
}
.rep-card-metric-label{
  font-size: 0.74rem;
  opacity: 0.6;
}
.rep-card-metric-value{
  font-weight: 700;
  font-size: 0.95rem;
  direction: ltr;
  unicode-bidi: isolate;
  font-variant-numeric: tabular-nums;
}

/* Actions row */
.rep-card-actions{
  display: flex;
  align-items: center;
  gap: 8px;
  /* Visible overflow lets the rep-menu panel hang off the card when
     the portal append isn't loaded. */
  position: relative;
}
.rep-card-actions .btn-glass{ min-height: 40px; padding: 8px 14px; font-size: 0.86rem; }
.rep-card-actions .rep-action-primary{
  flex: 1 1 0;
  background: linear-gradient(135deg,
    rgba(var(--accent-rgb), 0.22),
    rgba(var(--accent-rgb), 0.10));
  border-color: rgba(var(--accent-rgb), 0.45);
}
.rep-card-actions .rep-action-primary::before{
  inset: -4px;
  filter: blur(20px);
  background: radial-gradient(circle at center, rgba(var(--accent-rgb), 0.55), transparent 70%);
}
.rep-card-actions .rep-action-secondary,
.rep-card-actions .rep-action-secondary-form > button{
  flex: 1 1 0;
}
.rep-action-glyph{ font-size: 1rem; line-height: 1; }
.rep-action-secondary-form{ display: flex; flex: 1 1 0; margin: 0; }
.rep-action-secondary-form > .btn-glass{ width: 100%; }

/* Overflow trigger sits at the trailing edge of the actions row. */
.rep-card-actions .rep-menu{
  flex-shrink: 0;
}
.rep-card-actions .rep-menu-trigger{
  min-height: 40px;
  width: 40px;
  height: 40px;
  padding: 0;
  /* Reset min-width from .btn-glass.icon-only — keeps the chip square. */
  min-width: 40px;
  font-size: 1.1rem;
  cursor: pointer;
  /* Hide the native disclosure triangle. */
  list-style: none;
}
.rep-card-actions .rep-menu-trigger::-webkit-details-marker{ display: none; }
.rep-card-actions .rep-menu-trigger::marker{ content: ""; }

/* UI-0279: at very narrow widths (≤360px) the primary + secondary + overflow
   trigger could overflow the card on one line. Allow the row to wrap and let
   the primary action take its own full-width row; the secondary + ⋯ trigger
   then share the next row. */
@media (max-width: 360px){
  .rep-card-actions{ flex-wrap: wrap; }
  .rep-card-actions .rep-action-primary{ flex: 1 1 100%; }
}

/* Overflow panel — when the portal append is loaded, it lives in <body>;
   when it's NOT, it lives inside the card. Both code paths share these
   visual rules via .rep-menu-panel. */
.rep-menu-panel{
  position: absolute;
  z-index: 50;
  min-width: 200px;
  margin-top: 8px;
  inset-inline-end: 0;
  background: rgba(20,22,32,0.95);
  backdrop-filter: blur(22px) saturate(1.4);
  -webkit-backdrop-filter: blur(22px) saturate(1.4);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 14px;
  box-shadow: 0 16px 48px rgba(0,0,0,0.45);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* The HTML [hidden] attribute is the closed-state contract: the panel
   ships hidden and app.js's RepMenu toggles it (removeAttribute on open,
   setAttribute on close). But [hidden] only hides via the UA rule
   [hidden]{display:none}, which any author `display` declaration beats
   regardless of specificity — so `.rep-menu-panel{display:flex}` above
   was leaving every panel visible on load (piled under the cards) and
   defeating the JS close path. Re-assert the closed state at higher
   specificity (class + attribute) so it wins over the base rule and
   `body > .rep-menu-panel` alike. */
.rep-menu-panel[hidden]{ display: none; }
/* When portaled to <body>, the JS controls top/left in px using
   viewport-relative coords from getBoundingClientRect(). Switch to
   position:fixed so those coords land near the trigger instead of being
   interpreted document-relative (which placed the panel near the top
   of the page once the user scrolled). */
body > .rep-menu-panel{
  position: fixed;
  inset-inline-end: auto;
  margin-top: 0;
}
html[data-theme="light"] .rep-menu-panel{
  background: rgba(255,255,255,0.96);
  border-color: rgba(0,0,0,0.06);
  box-shadow: 0 16px 48px rgba(0,0,0,0.14);
}

.rep-menu-form{ margin: 0; }
.rep-menu-item{
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;     /* WCAG 2.5.5: 44 px touch target */
  padding: 10px 12px;
  background: transparent;
  border: none;
  border-radius: 10px;
  color: var(--text);
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 500;
  text-align: start;
  text-decoration: none;
  cursor: pointer;
}
.rep-menu-item:hover,
.rep-menu-item:focus-visible{
  background: rgba(255,255,255,0.06);
  outline: none;
}
html[data-theme="light"] .rep-menu-item:hover,
html[data-theme="light"] .rep-menu-item:focus-visible{
  background: rgba(0,0,0,0.04);
}
.rep-menu-glyph{ font-size: 1.05rem; line-height: 1; opacity: 0.95; flex-shrink: 0; }
.rep-menu-item.danger{
  color: rgb(248,113,113);
}
.rep-menu-item.danger:hover,
.rep-menu-item.danger:focus-visible{
  background: rgba(var(--danger-rgb), 0.12);
  color: rgb(248,113,113);
}
html[data-theme="light"] .rep-menu-item.danger{ color: rgb(185,28,28); }
html[data-theme="light"] .rep-menu-item.danger:hover,
html[data-theme="light"] .rep-menu-item.danger:focus-visible{ color: rgb(185,28,28); }

/* Open-state polish — fade the panel in. The native <details> toggle
   handles the visibility; we just animate the entry. */
details.rep-menu[open] > .rep-menu-panel,
body > .rep-menu-panel{
  animation: rep-menu-in 160ms ease-out;
}
@keyframes rep-menu-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Stacking-context fix — each .glass-rep-card establishes its own
   stacking context (backdrop-filter), so a menu panel inside card N
   would otherwise be painted UNDER card N+1 regardless of the panel's
   own z-index. Lift the whole card that holds the open menu above its
   siblings. Pair :has() with a relative + isolation baseline so we
   never depend on source order. */
.glass-rep-card{ isolation: isolate; }
.glass-rep-card:has(details.rep-menu[open]){ z-index: 60; }
/* Bring the actions row of the open card above the next card too —
   the trigger sits in this row and the panel anchors off it. */
.glass-rep-card:has(details.rep-menu[open]) .rep-card-actions{ z-index: 2; }

/* ═══════════════════════════════════════════════════════════════════════
   §48  4-STEP PROGRESS BAR — fixes-merged-02 / Issue 3b
   ═══════════════════════════════════════════════════════════════════════
   Used by admin/buy.php. Four .step-seg pills inside .glass-step-progress,
   each transitioning between default / .active / .done states. The bar is
   purely visual — it tracks progress, it doesn't gate submission. */

.glass-step-progress{
  display: flex;
  gap: 6px;
  padding: 8px 10px;
  margin-bottom: 14px;
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 12px;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
html[data-theme="light"] .glass-step-progress{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.05);
}
/* fixes-merged-02 / Section C: three states must be VISUALLY DISTINCT.
   The previous revision blended active (purple→green gradient) into done
   (green) — they read as the same colour. Fix: pure tokens.
     - default  low-contrast grey, ~30-40% opacity
     - done     saturated --success green, full opacity
     - active   pure --accent purple, full opacity + inner glow
   A side-by-side done+active row must read as two different colours. */
.glass-step-progress .step-seg{
  flex: 1;
  height: 7px;
  border-radius: 999px;
  background: rgba(255,255,255,0.10);
  transition:
    background 320ms cubic-bezier(0.4, 0, 0.2, 1),
    box-shadow 320ms cubic-bezier(0.4, 0, 0.2, 1);
}
html[data-theme="light"] .glass-step-progress .step-seg{
  background: rgba(0,0,0,0.10);
}
.glass-step-progress .step-seg.done{
  background: rgb(var(--success-rgb));
  box-shadow: 0 0 10px rgba(var(--success-rgb), 0.55);
}
.glass-step-progress .step-seg.active{
  /* Pure accent purple (NOT a gradient that bleeds toward green). */
  background: rgb(var(--accent-rgb));
  box-shadow:
    0 0 14px rgba(var(--accent-rgb), 0.70),
    inset 0 0 6px rgba(255,255,255,0.22);
}

/* ═══════════════════════════════════════════════════════════════════════
   §49  LEGACY PAGE CLASSES — carried forward from fixes-merged
   ═══════════════════════════════════════════════════════════════════════
   Kept so pages that haven't migrated to the glass system (search,
   payment_methods, settings, plus a handful of helper classes referenced
   by representatives.php / wallet.php / buy.php inline styles) continue
   to render with the new design tokens.
   ═══════════════════════════════════════════════════════════════════════ */

/* search.php's grid container */
.toolbox-grid {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 768px)  { .toolbox-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .toolbox-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* Persian faint helpers — used by wallet.php, buy.php, representatives.php */
.faint       { opacity: 0.7; }
.faint-deep  { opacity: 0.55; }

/* A-E: pre-glass .page-head rule removed — bare .page-head is unused
   in admin/, and this rule was fighting the canonical .glass-page-head
   declaration above when the same element carried both classes. */
.page-title h2 { margin-top: 0; }
.page-title p  { opacity: 0.7; font-size: 0.88rem; margin-top: 4px; }

/* A-F: .mt-*/.mb-* duplicates removed — app.css declares the same
   utilities via design tokens (var(--sp-2/4/5)). The previous glass.css
   copy used `!important` with slightly different magnitudes (14 vs 16),
   forcing every spacing utility to 14px and creating design-token drift.
   Keep .mt-0 + .text-center which app.css doesn't define. */
.mt-0 { margin-top: 0 !important; }
.text-center { text-align: center; }

/* representatives.php — .form-control is referenced by the original
   username input and a few inputs in modals. */
.form-control{
  width: 100%;
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 10px;
  padding: 10px 12px;
  color: var(--text);
  font-size: 0.9rem;
  font-family: inherit;
}
.form-control:focus{
  outline: none;
  border-color: rgba(var(--accent-rgb), 0.45);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.12);
}
html[data-theme="light"] .form-control{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.08);
}

/* buy.php — .buy-vol-warn is hidden by default, shown via .show.
   The IIFE at the bottom of buy.php toggles the .show class. */
.glass-alert.warn.buy-vol-warn { display: none; }
.glass-alert.warn.buy-vol-warn.show { display: flex; }

/* buy.php §1 — auto-username glass toggle.
   Compact row: a Persian label + a small square "checkbox" pill. The real
   <input type=checkbox> is visually hidden but stays keyboard/SR-accessible.
   Checkbox is OUTSIDE the <label> in source order so we can drive both the
   label row's hover/focus state AND the sibling illustration block from one
   `:checked` selector. */
.auto-username-check{
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}
.auto-username-row{
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px 6px 6px;
  border-radius: 12px;
  cursor: pointer;
  user-select: none;
  color: var(--text);
  font-weight: 600;
  font-size: 0.92rem;
  font-family: inherit;
  transition: background 0.18s ease, color 0.18s ease;
}
.auto-username-row:hover{ background: rgba(255,255,255,0.04); }
.auto-username-text{
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.4;
}
.auto-username-mark{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px; height: 26px;
  border-radius: 8px;
  background: rgba(255,255,255,0.04);
  border: 1.4px solid rgba(255,255,255,0.18);
  color: #fff;
  flex-shrink: 0;
  transition: background 0.2s ease, border-color 0.2s ease,
              box-shadow 0.25s ease, transform 0.15s ease;
}
.auto-username-mark svg{
  width: 14px; height: 14px;
  opacity: 0;
  transform: scale(0.55);
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.auto-username-check:checked ~ .auto-username-row .auto-username-mark{
  background: linear-gradient(135deg,
    rgb(var(--success-rgb)),
    rgba(var(--success-rgb), 0.85));
  border-color: rgba(var(--success-rgb), 0.95);
  box-shadow: 0 0 0 3px rgba(var(--success-rgb), 0.18),
              0 4px 14px rgba(var(--success-rgb), 0.40);
}
.auto-username-check:checked ~ .auto-username-row .auto-username-mark svg{
  opacity: 1;
  transform: scale(1);
}
.auto-username-check:focus-visible ~ .auto-username-row .auto-username-mark{
  outline: 2px solid rgba(255,255,255,0.32);
  outline-offset: 2px;
}
/* Manual username input fades while auto is on. */
.form-control.is-auto-locked,
.form-control:disabled{
  opacity: 0.55;
  cursor: not-allowed;
}
html[data-theme="light"] .auto-username-mark{
  background: rgba(0,0,0,0.03);
  border-color: rgba(0,0,0,0.18);
}
html[data-theme="light"] .auto-username-row:hover{ background: rgba(0,0,0,0.03); }

/* buy.php / wallet.php — form-hint utility */
.form-hint{
  font-size: 0.82rem;
  opacity: 0.7;
  margin: 4px 0 0;
}

/* End of legacy compat block. */


/* ═══════════════════════════════════════════════════════════════════════
   §50  BODY BLOBS — fixes-merged-02 / Section D1
   ═══════════════════════════════════════════════════════════════════════
   Large blurred colour orbs behind every page. Glass surfaces sit OVER
   these blobs and filter them via backdrop-filter — without them, every
   .glass-* card on a near-black body just reads as a flat dark
   rectangle, no matter how much rgba/blur the surface has.

   Skipped when the user has uploaded a custom background image
   (body.has-app-bg from partials/nav.php) — that image is its own
   colour layer underneath the glass.
   ═══════════════════════════════════════════════════════════════════════ */

:root{
  --blob-1: 139 92 246;   /* violet  */
  --blob-2: 251 146 60;   /* peach   */
  --blob-3: 167 139 250;  /* lavender */
  --blob-opacity: 0.20;

  /* H1.16: glass blur tokens. The codebase scatters six different blur
     magnitudes across visually-similar surfaces (10/12/14/18/20/24/28
     px). These three tokens collapse the most common families. Values
     match the existing hardcoded numbers on the canonical anchors:
       --glass-blur-card    → .glass-card (20px)
       --glass-blur-row     → .glass-row, .filters-glass (12px)
       --glass-blur-control → .btn-glass, form-control family (10px)
     Only safe, exact-match call sites use the tokens (see §2/§3/§13);
     other blur magnitudes are intentionally left alone so visual
     identity is preserved. */
  --glass-blur-card:    20px;
  --glass-blur-row:     12px;
  --glass-blur-control: 10px;
}
html[data-theme="light"]{
  --blob-1: 167 139 250;
  --blob-2: 251 146 60;
  --blob-3: 99 102 241;
  --blob-opacity: 0.35;
}

/* Default page background colour — ensures the blob layer has something
   to render against even before app.css has applied its own --bg. */
body{ background-color: var(--bg, #0a0b14); }

body:not(.has-app-bg)::before{
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background:
    radial-gradient(ellipse 60% 50% at 18% 22%, rgb(var(--blob-1) / var(--blob-opacity)) 0%, transparent 65%),
    radial-gradient(ellipse 55% 45% at 82% 18%, rgb(var(--blob-2) / calc(var(--blob-opacity) * 0.85)) 0%, transparent 65%),
    radial-gradient(ellipse 60% 55% at 50% 92%, rgb(var(--blob-3) / calc(var(--blob-opacity) * 0.90)) 0%, transparent 65%);
}

/* Re-usable hook for preview frames and any future place that needs
   blobs without being on <body>. preview.html applies this to each
   viewport stage so the blobs render inside the simulated phone. */
.glass-app-bg{ position: relative; isolation: isolate; }
.glass-app-bg::before{
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(ellipse 60% 50% at 18% 22%, rgb(var(--blob-1) / var(--blob-opacity)) 0%, transparent 65%),
    radial-gradient(ellipse 55% 45% at 82% 18%, rgb(var(--blob-2) / calc(var(--blob-opacity) * 0.85)) 0%, transparent 65%),
    radial-gradient(ellipse 60% 55% at 50% 92%, rgb(var(--blob-3) / calc(var(--blob-opacity) * 0.90)) 0%, transparent 65%);
}
.glass-app-bg > *{ position: relative; z-index: 1; }

/* ═══════════════════════════════════════════════════════════════════════
   §51  STICKY MODAL CTA — fixes-merged-02 / Section B (Issue 1)
   ═══════════════════════════════════════════════════════════════════════
   The three modal-submit CTAs that exist in the unmodified files pin to
   the bottom of the scrolling .modal-box so the user never has to scroll
   to discover the submit. All three selectors below match button classes
   ALREADY present in the unpatched files — no markup change required:

     .cta-deposit             admin/wallet.php  top-up submit
     .wallet-adjust-submit    admin/representatives.php  adjust modal submit
     .cta-buy                 admin/buy.php  (not in a modal — it's a
                              page-level form. Mobile sticky already exists
                              via the RULE-8 sticky-CTA rule at the bottom
                              of the file. Excluded here.)

   Forward-compatible: a future <div class="modal-actions"> wrapper —
   if and when it ships — gets the full glass-strip treatment via the
   trailing rule. Both forms coexist; the wrapper is optional.

   NOTE (rev 4): §53 below narrows the sticky CTA to a centred pill
   (max-width 280/320 px). The sticky position + heavy blur scaffolding
   below still applies; only the width is overridden.
   ═══════════════════════════════════════════════════════════════════════ */

.modal-overlay.glass-modal .modal-box .btn-glass.cta-deposit,
.modal-overlay.glass-modal .modal-box .btn-glass.wallet-adjust-submit,
.modal-overlay.glass-modal .modal-box .modal-actions{
  position: sticky;
  bottom: 0;
  z-index: 8;
  /* The button's own gradient + this backdrop-filter together blur
     anything scrolling under the button rectangle. */
  backdrop-filter: blur(60px) saturate(1.7);
  -webkit-backdrop-filter: blur(60px) saturate(1.7);
  /* Hairline above + soft drop below + inset highlight along the top
     edge. Together these read as a pinned glass bar that "catches light"
     where it meets the scrolling content above. */
  box-shadow:
    0 -1px 0 0 rgba(255,255,255,0.10),
    0 -14px 28px -10px rgba(0,0,0,0.55),
    inset 0 1px 0 rgba(255,255,255,0.12);
}
html[data-theme="light"] .modal-overlay.glass-modal .modal-box .btn-glass.cta-deposit,
html[data-theme="light"] .modal-overlay.glass-modal .modal-box .btn-glass.wallet-adjust-submit,
html[data-theme="light"] .modal-overlay.glass-modal .modal-box .modal-actions{
  box-shadow:
    0 -1px 0 0 rgba(0,0,0,0.08),
    0 -14px 28px -10px rgba(0,0,0,0.14),
    inset 0 1px 0 rgba(255,255,255,0.55);
}
html[data-theme="light"] .modal-overlay.glass-modal .modal-box .btn-glass.cta-deposit{
  /* Light theme — slightly lower alpha so the green stays bright on white. */
  background-color: rgba(var(--success-rgb), 0.42);
}
html[data-theme="light"] .modal-overlay.glass-modal .modal-box .btn-glass.wallet-adjust-submit,
html[data-theme="light"] .modal-overlay.glass-modal .modal-box .btn-glass.cta-buy{
  background-color: rgba(var(--accent-rgb), 0.42);
}

/* .modal-actions — forward-compatible wrapper. If a future patch wraps
   the modal submit in <div class="modal-actions">, this rule turns it
   into a full-width frosted strip spanning the modal-box edges. Optional
   in this package — see patches/wallet.php.md for the recommended
   minimal markup change. */
.modal-overlay.glass-modal .modal-box .modal-actions{
  display: flex;
  gap: 10px;
  align-items: stretch;
  /* Cancel the modal-box's 24px inner padding so the strip touches the side
     borders. UI-0044: that 24px is now the canonical .modal-box padding
     defined in §9 above (it previously referenced a non-existent app.css
     rule, so this -24px was cancelling padding that did not exist). */
  margin: 0 -24px;
  padding: 14px 24px max(14px, env(safe-area-inset-bottom, 14px));
  background: rgba(18,20,30,0.72);
  border-top: 1px solid rgba(255,255,255,0.08);
}
html[data-theme="light"] .modal-overlay.glass-modal .modal-box .modal-actions{
  background: rgba(255,255,255,0.82);
  border-top-color: rgba(0,0,0,0.06);
}
.modal-overlay.glass-modal .modal-box .modal-actions > .btn-glass{ flex: 1; }

/* UI-0158: the deposit-review popup is short and never scrolls, so the sticky
   frosted-strip treatment above reads as a stray bar floating mid-box. Reset
   it to a plain button row. Moved here from an inline <style> in
   admin/deposits.php (the page <style> loaded after glass.css and silently
   overrode the strip); the ID selector keeps the same specificity/behavior. */
#modal-deposit-review .modal-actions{
  position: static;
  display: flex;
  gap: 8px;
  margin: 0;
  padding: 0;
  background: transparent;
  border-top: 0;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
#modal-deposit-review .modal-actions > .btn-glass{ flex: 1; }

/* ═══════════════════════════════════════════════════════════════════════
   §52  REDUCED-TRANSPARENCY / NO-BACKDROP-FILTER FALLBACK
   ═══════════════════════════════════════════════════════════════════════
   Disables blur + blobs and collapses surfaces to opaque tinted versions.
   Form factor + dimensions unchanged — only the glass effect is gone.
   ═══════════════════════════════════════════════════════════════════════ */

@media (prefers-reduced-transparency: reduce) {
  body:not(.has-app-bg)::before,
  .glass-app-bg::before{ display: none; }

  .glass-card, .glass-stat-card, .glass-page-head, .glass-info-bar,
  .glass-form-section, .glass-balance-card, .glass-method-card,
  .glass-deposit-row, .glass-tx-table, .glass-rep-card, .rep-menu-panel,
  .filters-glass, .btn-glass, .glass-bottom-nav, .glass-amount-input,
  .glass-textarea, .glass-step-progress, .glass-stepper .stepper-input,
  .modal-overlay.glass-modal .modal-box,
  .modal-overlay.glass-modal .modal-box .btn-glass.cta-deposit,
  .modal-overlay.glass-modal .modal-box .btn-glass.wallet-adjust-submit,
  .modal-overlay.glass-modal .modal-box .modal-actions{
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }

  .glass-card, .glass-stat-card, .glass-page-head, .glass-info-bar,
  .glass-form-section, .glass-balance-card, .glass-method-card,
  .glass-deposit-row, .glass-rep-card, .filters-glass, .glass-bottom-nav,
  .modal-overlay.glass-modal .modal-box, .rep-menu-panel{
    background: var(--elevated, #111322) !important;
  }
  html[data-theme="light"] .glass-card,
  html[data-theme="light"] .glass-stat-card,
  html[data-theme="light"] .glass-page-head,
  html[data-theme="light"] .glass-info-bar,
  html[data-theme="light"] .glass-form-section,
  html[data-theme="light"] .glass-balance-card,
  html[data-theme="light"] .glass-method-card,
  html[data-theme="light"] .glass-deposit-row,
  html[data-theme="light"] .glass-rep-card,
  html[data-theme="light"] .filters-glass,
  html[data-theme="light"] .glass-bottom-nav,
  html[data-theme="light"] .modal-overlay.glass-modal .modal-box,
  html[data-theme="light"] .rep-menu-panel{
    background: #ffffff !important;
  }
}

/* Browsers that don't support backdrop-filter at all (older Edge,
   Firefox < 103 without flag). Same opaque collapse. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass-card, .glass-stat-card, .glass-page-head, .glass-info-bar,
  .glass-form-section, .glass-balance-card, .glass-method-card,
  .glass-deposit-row, .glass-rep-card, .filters-glass, .glass-bottom-nav,
  .modal-overlay.glass-modal .modal-box, .rep-menu-panel{
    background: var(--elevated, #111322);
  }
}

/* ═════════════════════════════════════════════════════════════════
   §53  STICKY MODAL CTA — CENTRED PILL  (narrows §51's full-bleed)
   ═════════════════════════════════════════════════════════════════
   The button keeps its §30 / §22 design (green/indigo gradient + glow)
   and just gets a narrower footprint so the sticky CTA reads as a
   centred pill rather than a full-bleed bar. Width caps at 280 px on
   phones and 320 px on tablet+. §51's sticky-position + heavy blur
   scaffolding still applies — the centred pill just sits on top of
   the scrolling content.
   ═════════════════════════════════════════════════════════════════ */
.modal-overlay.glass-modal .modal-box .btn-glass.cta-deposit,
.modal-overlay.glass-modal .modal-box .btn-glass.wallet-adjust-submit,
.modal-overlay.glass-modal .modal-box .btn-glass.cta-buy{
  width: auto;
  max-width: 320px;
  margin-left: auto;
  margin-right: auto;
  display: block;
}
@media (max-width: 480px){
  .modal-overlay.glass-modal .modal-box .btn-glass.cta-deposit,
  .modal-overlay.glass-modal .modal-box .btn-glass.wallet-adjust-submit,
  .modal-overlay.glass-modal .modal-box .btn-glass.cta-buy{
    max-width: 280px;
  }
}

/* ─────────────────────────────────────────────────────────────────
   §53b  TOP-UP CTA — FULL-WIDTH (overrides §53's centred pill)
   ─────────────────────────────────────────────────────────────────
   In admin/wallet.php's top-up modal the submit ("ثبت درخواست") sat
   next to full-width controls (select / amount / receipt / note), so
   the §53 centred pill read as undersized + off-balance against them.
   Restore the §30 full-bleed footprint so the CTA matches the form
   controls above it on phone + desktop alike. Scoped to #modal-topup
   (the only .cta-deposit usage) — the buy/adjust modal pills keep §53.
   The §51 sticky-bottom + frosted-bar scaffolding still applies, so
   this reads as a full-width sticky submit bar. The ID selector
   outranks §53's class chains on every breakpoint — no media query
   needed. */
#modal-topup .btn-glass.cta-deposit{
  display: flex;            /* re-enable .btn-glass's centring of the label */
  width: 100%;
  max-width: none;
  margin-left: 0;
  margin-right: 0;
}

/* ═════════════════════════════════════════════════════════════════
   §54  AMBIENT COLOUR ACCENTS ON GLASS SURFACES
   ═════════════════════════════════════════════════════════════════
   A barely-saturated radial wash on the main glass containers so the
   surfaces read as cooler/warmer rather than flat-white-on-flat-dark.
   The wash is layered as a background-image, leaving the existing
   solid background-color in place — so no surface loses contrast.
   Dark theme leans into the indigo/violet brand colour at low alpha;
   light theme uses indigo + a touch of cyan so the surfaces don't go
   muddy on the cream-coloured app background.
   ═════════════════════════════════════════════════════════════════ */
.glass-card,
.glass-rep-card,
.glass-form-section,
.glass-method-card,
.glass-info-bar,
.glass-page-head,
.filters-glass,
.glass-balance-card,
.glass-deposit-row{
  background-image:
    radial-gradient(140% 90% at 0% 0%,    rgb(139 92 246 / 0.07), transparent 55%),
    radial-gradient(140% 90% at 100% 100%, rgb( 99 102 241 / 0.05), transparent 60%);
}
html[data-theme="light"] .glass-card,
html[data-theme="light"] .glass-rep-card,
html[data-theme="light"] .glass-form-section,
html[data-theme="light"] .glass-method-card,
html[data-theme="light"] .glass-info-bar,
html[data-theme="light"] .glass-page-head,
html[data-theme="light"] .filters-glass,
html[data-theme="light"] .glass-balance-card,
html[data-theme="light"] .glass-deposit-row{
  background-image:
    radial-gradient(140% 90% at 0% 0%,    rgb(139 92 246 / 0.09), transparent 55%),
    radial-gradient(140% 90% at 100% 100%, rgb(  6 182 212 / 0.07), transparent 60%);
}
/* Modal panel — same recipe but slightly stronger so the modal reads
   as a richer pane than a plain card. */
.modal-overlay.glass-modal .modal-box{
  background-image:
    radial-gradient(120% 70% at 0% 0%,    rgb(139 92 246 / 0.10), transparent 55%),
    radial-gradient(120% 70% at 100% 100%, rgb( 99 102 241 / 0.07), transparent 60%);
}
html[data-theme="light"] .modal-overlay.glass-modal .modal-box{
  background-image:
    radial-gradient(120% 70% at 0% 0%,    rgb(139 92 246 / 0.10), transparent 55%),
    radial-gradient(120% 70% at 100% 100%, rgb(  6 182 212 / 0.08), transparent 60%);
}
/* Reduced-transparency override — flatten back to the elevated solid. */
@media (prefers-reduced-transparency: reduce){
  .glass-card, .glass-rep-card, .glass-form-section, .glass-method-card,
  .glass-info-bar, .glass-page-head, .filters-glass, .glass-balance-card,
  .glass-deposit-row, .modal-overlay.glass-modal .modal-box{
    background-image: none !important;
  }
}

/* ═════════════════════════════════════════════════════════════════
   §55  INFO-BANNER NARROW — modal-scoped
   ═════════════════════════════════════════════════════════════════
   The .glass-alert.info banner inside a modal was stretching the full
   modal width, which made the modal feel visually wider than it should.
   Cap its width + centre so the banner reads as a contained pill
   sitting ABOVE the form rather than as a full-bleed strip. Scoped to
   .modal-box so the same alert on regular pages is unchanged.
   ═════════════════════════════════════════════════════════════════ */
.modal-overlay.glass-modal .modal-box > .glass-alert.info,
.modal-overlay.glass-modal .modal-box form > .glass-alert.info{
  max-width: 360px;
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}

/* End of fixes-merged-02 additions. */

/* ═══════════════════════════════════════════════════════════════════
   §56  AUTH REDESIGN  (login.php — body.auth-redesign)
   ───────────────────────────────────────────────────────────────────
   Scoped under .auth-redesign so it cannot affect any other surface.
   Layout:
     <main.auth-shell>
       <header.auth-hero>  ← sparkle + brand + subtitle (above card)
       <section.glass-auth-card.auth-card>
         <h2.auth-card-title>
         [<glass-alert>?]
         <form.auth-form>
           <.auth-field>
             <label>
             <.auth-field-row>
               <.auth-icon-chip>
               <.auth-input-wrap[.has-eye]>
                 <input.auth-input>
                 [<button.auth-eye-btn>]
         <.auth-cta>
         <.auth-divider>
         <a.auth-secondary-cta>
         <p.auth-hint>
   ═══════════════════════════════════════════════════════════════════ */

body.auth-page.auth-redesign{
  display: block;
  padding: 0;
  min-height: 100vh;
  /* Slightly deeper background so the radial blobs read with more contrast */
  background: var(--auth-bg);
}
body.auth-page.auth-redesign::before{
  /* Replace the default 3-blob field with a calmer two-blob composition.
     The blobs are positioned so the hero gets a faint top-left lift and
     the card sits in front of a soft accent halo. */
  background:
    radial-gradient(46% 38% at 18% 22%,  var(--auth-blob1),  transparent 65%),
    radial-gradient(42% 34% at 82% 78%,  var(--auth-blob2),  transparent 65%),
    radial-gradient(58% 40% at 50% 100%, var(--auth-blob3),  transparent 70%);
  filter: blur(8px);
}

.auth-redesign .auth-shell{
  position: relative;
  z-index: 1;
  min-height: 100vh;
  width: 100%;
  max-width: 480px;
  margin-inline: auto;
  padding: clamp(24px, 5vh, 56px) clamp(16px, 4vw, 28px);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: center;
  gap: clamp(24px, 4vh, 40px);
}

/* ─── Floating theme toggle (auth-only positioning) ─────────────── */
.auth-redesign .auth-theme-toggle{
  position: fixed;
  top: 16px;
  inset-inline-end: 16px;
  width: 38px; height: 38px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 12px;
  background: var(--toggle-btn-bg, rgba(255,255,255,0.08));
  border: 1px solid var(--auth-card-border, rgba(255,255,255,0.10));
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  cursor: pointer;
  font-size: 16px;
  color: var(--text);
  transition: transform .18s var(--ease), border-color .18s var(--ease);
  z-index: 5;
}
.auth-redesign .auth-theme-toggle:hover{
  transform: translateY(-1px);
  border-color: rgba(255,255,255,0.20);
}
/* UI-0339: on very narrow screens (≤380px) the fixed toggle crowded the
   centered hero. Pull it tighter into the corner and shrink it so it stays
   clear of the hero icon/title. */
@media (max-width: 380px){
  .auth-redesign .auth-theme-toggle{
    top: 8px;
    inset-inline-end: 8px;
    width: 34px; height: 34px;
    font-size: 14px;
  }
}

/* ─── Hero (outside the card) ───────────────────────────────────── */
.auth-redesign .auth-hero{
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding-top: 8px;
}
.auth-redesign .auth-hero-icon{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px; height: 56px;
  color: var(--accent-2);
  filter:
    drop-shadow(0 0 14px rgba(var(--accent-rgb), 0.55))
    drop-shadow(0 0 28px rgba(var(--accent-rgb), 0.25));
  margin-bottom: 4px;
}
.auth-redesign .auth-hero-title{
  font-size: clamp(2rem, 6.5vw, 2.6rem);
  font-weight: 800;
  letter-spacing: 0.5px;
  line-height: 1.1;
  color: var(--text);
  margin: 0;
}
.auth-redesign .auth-hero-sub{
  font-size: 0.95rem;
  color: var(--text-m);
  opacity: 0.85;
  margin: 0;
}

/* ─── Card overrides ────────────────────────────────────────────── */
.auth-redesign .auth-card{
  max-width: 100%;
  padding: clamp(22px, 4vw, 30px) clamp(20px, 4vw, 28px);
  border-radius: 24px;
  /* Softer, lighter glass than the default to match the target screenshot */
  background:
    linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02)),
    var(--auth-card, rgba(20,22,32,0.55));
  border: 1px solid rgba(255,255,255,0.10);
  box-shadow:
    0 24px 64px rgba(0,0,0,0.55),
    inset 0 1px 0 rgba(255,255,255,0.08);
}
.auth-redesign .auth-card::after{
  /* Slightly larger, slightly cooler halo than default */
  inset: -56px;
  background:
    radial-gradient(60% 50% at 20% 20%,  rgba(var(--accent-rgb), 0.22), transparent 60%),
    radial-gradient(50% 50% at 85% 85%,  rgba(34,211,238,0.14),         transparent 65%);
  filter: blur(50px);
}

.auth-redesign .auth-card-title{
  text-align: center;
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: 0.2px;
  color: var(--text);
  margin: 6px 0 20px;
}

/* ─── Form rows ─────────────────────────────────────────────────── */
.auth-redesign .auth-form{ display: flex; flex-direction: column; gap: 16px; }

.auth-redesign .auth-field{ display: flex; flex-direction: column; gap: 8px; }
.auth-redesign .auth-field > label{
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--text-m);
  /* Labels right-aligned in RTL — matches target */
  text-align: start;
  padding-inline-start: 4px;
}
.auth-redesign .auth-field > label .faint{ color: var(--text-d); font-weight: 400; }

.auth-redesign .auth-field-row{
  display: flex;
  align-items: stretch;
  gap: 10px;
  min-height: 52px;
}

/* Icon "chip" — small glass square that prefixes each input (visually on
   the right in RTL because it's the first flex child). */
.auth-redesign .auth-icon-chip{
  flex: 0 0 52px;
  width: 52px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 14px;
  color: var(--text-m);
  transition: color .18s var(--ease), border-color .18s var(--ease), background .18s var(--ease);
}
.auth-redesign .auth-field-row:focus-within .auth-icon-chip{
  color: var(--accent-2);
  border-color: rgba(var(--accent-rgb), 0.45);
  background: rgba(var(--accent-rgb), 0.10);
}

.auth-redesign .auth-input-wrap{
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
}
.auth-redesign .auth-input{
  width: 100%;
  min-height: 52px;
  padding: 0 16px;
  border-radius: 14px;
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.10);
  color: var(--text);
  font: inherit;
  font-size: 0.98rem;
  outline: none;
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease), background .18s var(--ease);
}
.auth-redesign .auth-input::placeholder{
  color: var(--text-d);
  /* Persian placeholder shouldn't fight the dir="ltr" of the actual value */
  direction: rtl; text-align: start;
  opacity: 0.85;
}
/* H1.15: inside a .has-eye wrap the placeholder must flow LTR so it
   starts at the input's leading edge and never overlaps the eye toggle
   sitting on the right (inline-start in an RTL page). The password
   placeholder is a row of dots, so direction has no aesthetic impact;
   it only changes which edge the row begins from. */
.auth-redesign .has-eye .auth-input::placeholder{
  direction: ltr;
  text-align: start;
}
/* UI-0045: an email placeholder (example@email.com) carries neutral chars
   (@ .) that the RTL default reorders into gibberish; flow it LTR so it
   reads correctly. The field is already dir="ltr". */
.auth-redesign input[type="email"]::placeholder{
  direction: ltr;
  text-align: start;
}
.auth-redesign .auth-input:focus{
  border-color: rgba(var(--accent-rgb), 0.55);
  background: rgba(255,255,255,0.06);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.15);
}
/* Eye-toggle slot: extra padding on the leading edge of the input so the
   value never collides with the absolutely-positioned eye button. In RTL
   the leading edge is the left, where the password value flows from. */
/* UI-0054: widen the eye button to a 44px touch target; bump the input's
   leading padding so the password value still clears the (now wider) button. */
.auth-redesign .has-eye .auth-input{ padding-inline-start: 52px; }

.auth-redesign .auth-eye-btn{
  position: absolute;
  inset-block: 0;
  inset-inline-start: 6px;
  width: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent;
  border: 0;
  color: var(--text-m);
  cursor: pointer;
  border-radius: 10px;
  transition: color .15s var(--ease), background .15s var(--ease);
}
.auth-redesign .auth-eye-btn:hover{
  color: var(--text);
  background: rgba(255,255,255,0.06);
}
.auth-redesign .auth-eye-btn:focus-visible{
  outline: 2px solid rgba(var(--accent-rgb), 0.45);
  outline-offset: 1px;
}

/* ─── Primary CTA (login / register submit) ─────────────────────────
   Dual-background gradient ring: fill painted to `padding-box`, then a
   second gradient painted to `border-box` shows through a transparent
   border. Result: a clearly visible purple→cyan rim that matches the
   target without relying on a faint 1-px solid. */
.auth-redesign .btn-glass.auth-cta{
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 100%;
  min-height: 54px;
  margin-top: 8px;
  padding: 14px 20px;
  border-radius: 16px;
  font-size: 1.02rem;
  font-weight: 700;
  letter-spacing: 0.3px;
  color: #ffffff;
  cursor: pointer;
  border: 1.5px solid transparent;
  background:
    linear-gradient(180deg,
      rgba(124,131,247,0.28),
      rgba(91,99,232,0.12)) padding-box,
    linear-gradient(135deg,
      #7c83f7 0%,
      #a78bfa 50%,
      #22d3ee 100%) border-box;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.20),
    0 14px 36px rgba(124,131,247,0.30);
  transition: transform .18s var(--ease),
              box-shadow .18s var(--ease),
              filter .18s var(--ease);
}
.auth-redesign .btn-glass.auth-cta::before{
  content: "";
  position: absolute;
  inset: -8px;
  border-radius: 22px;
  background: radial-gradient(60% 80% at 50% 50%,
    rgba(124,131,247,0.55), transparent 70%);
  filter: blur(26px);
  opacity: 0.55;
  z-index: -1;
  pointer-events: none;
  transition: opacity .18s var(--ease);
}
.auth-redesign .btn-glass.auth-cta:hover{
  transform: translateY(-1px);
  filter: brightness(1.06);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.24),
    0 18px 44px rgba(124,131,247,0.36);
}
.auth-redesign .btn-glass.auth-cta:hover::before{ opacity: 0.9; }
.auth-redesign .btn-glass.auth-cta:active{ transform: translateY(0); }
.auth-redesign .btn-glass.auth-cta:focus-visible{
  outline: 2px solid rgba(167,139,250,0.55);
  outline-offset: 3px;
}
.auth-redesign .btn-glass.auth-cta svg{
  /* Arrow naturally points right; in RTL we want it to read as "forward"
     toward the leading edge, so flip horizontally. */
  transform: scaleX(-1);
  opacity: 0.95;
}

/* ─── "یا" divider ──────────────────────────────────────────────── */
.auth-redesign .auth-divider{
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 22px 0 16px;
  color: var(--text-d);
  font-size: 0.85rem;
}
.auth-redesign .auth-divider::before,
.auth-redesign .auth-divider::after{
  content: "";
  flex: 1;
  height: 1px;
  /* A-A: `to inline-start` is invalid CSS — the <side-or-corner> grammar
     accepts only physical keywords. Use `to left` to match the existing
     `.glass-divider` pattern at lines 497/501. */
  background: linear-gradient(to left, transparent, rgba(255,255,255,0.14), transparent);
}
.auth-redesign .auth-divider > span{ padding: 0 4px; }

/* ─── Secondary CTA (sign-up link box) ──────────────────────────── */
.auth-redesign .auth-secondary-cta{
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 100%;
  min-height: 52px;
  padding: 12px 18px;
  border-radius: 14px;
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.10);
  color: var(--text-m);
  text-decoration: none;
  font-size: 0.95rem;
  font-weight: 500;
  transition: background .18s var(--ease), border-color .18s var(--ease), color .18s var(--ease), transform .18s var(--ease);
}
.auth-redesign .auth-secondary-cta:hover{
  background: rgba(255,255,255,0.06);
  border-color: rgba(var(--accent-rgb), 0.45);
  color: var(--text);
  transform: translateY(-1px);
}
.auth-redesign .auth-secondary-cta strong{
  color: var(--accent-3);
  font-weight: 700;
  margin-inline-start: 4px;
}
.auth-redesign .auth-secondary-cta svg{
  color: var(--text-d);
  flex-shrink: 0;
}

/* ─── Helper hint ───────────────────────────────────────────────── */
.auth-redesign .auth-hint{
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 14px;
  font-size: 0.8rem;
  color: var(--text-d);
  text-align: center;
}
.auth-redesign .auth-hint svg{ flex-shrink: 0; opacity: 0.8; }

/* ─── Light-theme adjustments ───────────────────────────────────── */
html[data-theme="light"] body.auth-page.auth-redesign{
  background: var(--auth-bg);
}
html[data-theme="light"] .auth-redesign .auth-card{
  background:
    linear-gradient(180deg, rgba(255,255,255,0.92), rgba(255,255,255,0.78)),
    var(--auth-card);
  border-color: var(--auth-card-border);
  box-shadow:
    0 18px 56px rgba(40,40,60,0.10),
    inset 0 1px 0 rgba(255,255,255,0.40);
}
html[data-theme="light"] .auth-redesign .auth-icon-chip,
html[data-theme="light"] .auth-redesign .auth-input,
html[data-theme="light"] .auth-redesign .auth-secondary-cta{
  background: #ffffff;
  border-color: var(--auth-input-border);
  color: var(--text);
}
html[data-theme="light"] .auth-redesign .auth-icon-chip{ color: var(--text-m); }
html[data-theme="light"] .auth-redesign .auth-input:focus{
  background: #ffffff;
  border-color: rgba(var(--accent-rgb), 0.65);
}
html[data-theme="light"] .auth-redesign .auth-divider::before,
html[data-theme="light"] .auth-redesign .auth-divider::after{
  /* A-A: see note above. */
  background: linear-gradient(to left, transparent, rgba(0,0,0,0.12), transparent);
}
html[data-theme="light"] .auth-redesign .btn-glass.auth-cta{
  color: #ffffff;
  /* Solid coloured fill on light theme — the gradient ring would
     disappear against a white card. The fill itself carries the brand
     gradient instead. */
  border: 1.5px solid transparent;
  background:
    linear-gradient(135deg,
      rgba(var(--accent-rgb), 0.95),
      rgba(139,92,246,0.92) 55%,
      rgba(8,145,178,0.88)) padding-box,
    linear-gradient(135deg,
      rgba(var(--accent-rgb), 1),
      #8b5cf6 50%,
      #0891b2 100%) border-box;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.28),
    0 10px 28px rgba(var(--accent-rgb), 0.32);
}
html[data-theme="light"] .auth-redesign .btn-glass.auth-cta::before{
  background: radial-gradient(60% 80% at 50% 50%,
    rgba(var(--accent-rgb), 0.32), transparent 70%);
  opacity: 0.5;
}

/* ─── Small screens ─────────────────────────────────────────────── */
@media (max-width: 380px){
  .auth-redesign .auth-icon-chip{ flex-basis: 46px; width: 46px; }
  .auth-redesign .auth-field-row{ gap: 8px; min-height: 48px; }
  .auth-redesign .auth-input{ min-height: 48px; padding: 0 12px; font-size: 0.94rem; }
  .auth-redesign .has-eye .auth-input{ padding-inline-start: 40px; }
  .auth-redesign .btn-glass.auth-cta{ min-height: 50px; font-size: 0.98rem; }
  .auth-redesign .auth-hero-title{ font-size: 1.9rem; }
}

/* Respect reduced-motion */
@media (prefers-reduced-motion: reduce){
  .auth-redesign .btn-glass.auth-cta,
  .auth-redesign .auth-secondary-cta,
  .auth-redesign .auth-icon-chip,
  .auth-redesign .auth-input{ transition: none; }
  .auth-redesign .btn-glass.auth-cta:hover,
  .auth-redesign .auth-secondary-cta:hover{ transform: none; }
}
/* End of §56 — Auth redesign. */

/* ───────────────────────── 57. Toast system ──────────────────────────────
   Container is created on first Toast.show() (assets/app.js). Sits above
   modal overlays (modal = 1000, theme-fab = 1100) so notifications stay
   visible while a modal is open. RTL: anchors to inline-end so the toast
   slides in from the same edge in both directions. */
#toast-container{
  position: fixed;
  inset-block-start: max(16px, env(safe-area-inset-top, 16px));
  inset-inline-end:  max(16px, env(safe-area-inset-right, 16px));
  z-index: 1200;
  display: flex;
  flex-direction: column;
  gap: 10px;
  pointer-events: none;
  max-width: min(360px, calc(100vw - 32px));
}
.toast{
  pointer-events: auto;
  /* UI-0287: flex row so the message and the dismiss button sit side by side. */
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 16px;
  border-radius: 12px;
  background: rgba(20,22,32,0.85);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  backdrop-filter: blur(18px) saturate(1.3);
  border: 1px solid rgba(255,255,255,0.10);
  box-shadow: 0 12px 32px rgba(0,0,0,0.45);
  color: var(--text);
  font-size: 0.95rem;
  line-height: 1.5;
  animation: pg-toast-in 0.22s cubic-bezier(0.2, 0.8, 0.2, 1) both;
  will-change: transform, opacity;
}
/* UI-0287: dismiss button — lets error toasts (which now linger longer) be
   closed immediately instead of waiting out the timer. */
.toast-msg{ flex: 1 1 auto; min-width: 0; }
.toast-close{
  flex: 0 0 auto;
  background: transparent;
  border: none;
  color: inherit;
  opacity: 0.6;
  cursor: pointer;
  font-size: 1.05rem;
  line-height: 1;
  padding: 2px 4px;
  margin: -2px -4px -2px 0;
  border-radius: 6px;
}
.toast-close:hover{ opacity: 1; }
.toast-close:focus-visible{ outline: 2px solid rgba(255,255,255,0.4); outline-offset: 1px; opacity: 1; }
.toast.info   { border-color: rgba(var(--accent-rgb),  0.45); box-shadow: 0 12px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(var(--accent-rgb),  0.25); }
.toast.success{ border-color: rgba(var(--success-rgb), 0.45); box-shadow: 0 12px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(var(--success-rgb), 0.25); }
.toast.warn   { border-color: rgba(var(--warn-rgb),    0.45); box-shadow: 0 12px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(var(--warn-rgb),    0.25); }
.toast.error  { border-color: rgba(var(--danger-rgb),  0.45); box-shadow: 0 12px 32px rgba(0,0,0,0.45), 0 0 0 1px rgba(var(--danger-rgb),  0.25); }
.toast.hide{
  animation: pg-toast-out 0.28s cubic-bezier(0.4, 0, 0.6, 1) both;
}
@keyframes pg-toast-in{
  from{ opacity: 0; transform: translateX(12px) translateY(-4px); }
  to  { opacity: 1; transform: translateX(0)   translateY(0);    }
}
@keyframes pg-toast-out{
  from{ opacity: 1; transform: translateX(0)   translateY(0);    }
  to  { opacity: 0; transform: translateX(12px) translateY(-4px); }
}
html[data-theme="light"] .toast{
  background: rgba(255,255,255,0.92);
  border-color: rgba(0,0,0,0.08);
  color: var(--text);
}
@media (prefers-reduced-motion: reduce){
  .toast, .toast.hide{ animation-duration: 0.001s; }
}
/* End of §57 — Toast system. */

/* ═══════════════════════════════════════════════════════════════════════
   §58  BODY SCROLL LOCK + INERT BACKGROUND (H1.4, H1.9)
   ═══════════════════════════════════════════════════════════════════════
   When any glass-modal is open, app.js toggles body.modal-open. We freeze
   page scroll behind the modal on Android Chrome + iOS Safari, and also
   knock the theme-FAB out of the way so it cannot float above the overlay
   (H1.2). The actual focus trap lives in app.js. */
body.modal-open{
  /* UI-0038: keep the scroll-lock (overflow:hidden + overscroll-behavior)
     but DROP `touch-action: none` — that disabled pinch-zoom for the whole
     body, including modal content, so operators couldn't zoom a deposit
     receipt to verify it on iOS Safari. overflow:hidden already prevents the
     page behind from scrolling. */
  overflow: hidden;
  overscroll-behavior: contain;
}
body.modal-open .theme-fab{
  visibility: hidden;
  pointer-events: none;
}
/* Background siblings are flagged with [inert] from app.js. Belt-and-
   suspenders: also hide focus rings from anything inside an inert subtree
   so the visible focus indicator never lives behind the overlay. */
[inert], [inert] *{
  user-select: none;
  -webkit-user-select: none;
}

/* ═══════════════════════════════════════════════════════════════════════
   §59  GLASS CONFIRM MODAL (H1.10, H6.3)
   ═══════════════════════════════════════════════════════════════════════
   A single reusable modal that replaces window.confirm() for every
   data-confirm action. app.js owns the open/close lifecycle and wires
   the destructive variant. Markup is injected once into <body>; the
   selectors below style that template. The host element is a regular
   .modal-overlay.glass-modal so §9 + §58 already cover scroll-lock and
   the bottom-sheet treatment on mobile. */
#glass-confirm-modal .modal-box{
  width: min(440px, calc(100vw - 32px));
  padding: 22px 24px;
  gap: 16px;
}
@media (max-width: 768px){
  #glass-confirm-modal .modal-box{
    width: 100%;
    padding: 20px 20px max(20px, env(safe-area-inset-bottom, 20px));
  }
}
.glass-confirm-icon{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 14px;
  font-size: 1.5rem;
  background: rgba(var(--warn-rgb), 0.14);
  border: 1px solid rgba(var(--warn-rgb), 0.35);
  margin-bottom: 4px;
  flex-shrink: 0;
}
#glass-confirm-modal.is-danger .glass-confirm-icon{
  background: rgba(var(--danger-rgb), 0.14);
  border-color: rgba(var(--danger-rgb), 0.40);
}
.glass-confirm-title{
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--text);
  /* Persian text often runs longer than English — let it wrap. */
  word-break: break-word;
}
.glass-confirm-message{
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--text-m, var(--text));
  opacity: 0.85;
  word-break: break-word;
}
.glass-confirm-actions{
  display: flex;
  gap: 10px;
  margin-top: 6px;
  flex-wrap: wrap;
}
.glass-confirm-actions > .btn-glass{
  flex: 1 1 0;
  min-width: 0;
  min-height: 46px;
}
@media (max-width: 380px){
  .glass-confirm-actions{ flex-direction: column-reverse; }
  .glass-confirm-actions > .btn-glass{ width: 100%; }
}

/* ═══════════════════════════════════════════════════════════════════════
   §60  REP MENU — BUTTON CONTROLLER (H1.3, H1.19, H6.4)
   ═══════════════════════════════════════════════════════════════════════
   The mobile rep-card overflow trigger is being migrated from
   <details>/<summary> to a <button aria-haspopup="menu" aria-expanded>.
   These selectors mirror the visual rules used for the details version,
   so legacy unopened cards still render correctly while app.js drives
   the new accessible controller. */
.rep-card-actions .rep-menu-button{
  appearance: none;
  -webkit-appearance: none;
  min-height: 40px;
  width: 40px;
  height: 40px;
  padding: 0;
  min-width: 40px;
  font-size: 1.1rem;
  cursor: pointer;
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 12px;
  color: var(--text);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.rep-card-actions .rep-menu-button:hover,
.rep-card-actions .rep-menu-button:focus-visible{
  background: rgba(255,255,255,0.10);
  outline: none;
  border-color: rgba(var(--accent-rgb), 0.45);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.18);
}
html[data-theme="light"] .rep-card-actions .rep-menu-button{
  background: rgba(0,0,0,0.04);
  border-color: rgba(0,0,0,0.10);
}
html[data-theme="light"] .rep-card-actions .rep-menu-button:hover,
html[data-theme="light"] .rep-card-actions .rep-menu-button:focus-visible{
  background: rgba(0,0,0,0.06);
}
.rep-card-actions .rep-menu-button[aria-expanded="true"]{
  background: rgba(var(--accent-rgb), 0.18);
  border-color: rgba(var(--accent-rgb), 0.45);
}

/* Lift the open card above the bottom-nav (z-index 150). The panel itself
   gets z-index 200 so it floats over every mobile chrome surface. */
.rep-menu-panel{ z-index: 200; }
body > .rep-menu-panel{ z-index: 1050; }
.glass-rep-card:has(.rep-menu-button[aria-expanded="true"]){ z-index: 200; }
.glass-rep-card:has(.rep-menu-button[aria-expanded="true"]) .rep-card-actions{ z-index: 3; }

/* Truncated rep-card usernames need a visible affordance — pair `title=`
   on the markup with a slight underline-on-hover so operators know they
   can hover/tap-long-press to read the full string. */
.rep-card-name,
.rep-card-username{
  /* Keep the existing ellipsis rules from §40 of glass.css but allow the
     hover/tap state to expand on demand on small screens. */
  cursor: help;
}
/* H6.4: tap-to-expand. JS toggles .is-expanded on the same elements; this
   undoes the ellipsis and allows the string to wrap so the operator can
   read the full name/username on mobile without leaving the card. */
.rep-card-name.is-expanded,
.rep-card-username.is-expanded{
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* ═══════════════════════════════════════════════════════════════════════
   §61  RECEIPT PREVIEW — wallet upload + deposit review (H5.1, H5.5)
   ═══════════════════════════════════════════════════════════════════════
   Both surfaces share the same preview chip: a thumbnail (img or a PDF
   placeholder), the filename, and a "remove" link. The wallet upload zone
   shows it inside the drop zone; the deposit-review modal shows it above
   the action buttons so operators verify before approving. */
.receipt-preview{
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  margin-top: 10px;
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 12px;
  background: rgba(255,255,255,0.03);
}
html[data-theme="light"] .receipt-preview{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.08);
}
.receipt-preview .receipt-thumb{
  width: 56px;
  height: 56px;
  border-radius: 10px;
  object-fit: cover;
  background: rgba(0,0,0,0.20);
  flex-shrink: 0;
}
.receipt-preview .receipt-thumb.is-pdf{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.4rem;
  color: var(--text-m);
}
.receipt-preview .receipt-meta{
  flex: 1;
  min-width: 0;
  font-size: 0.85rem;
  line-height: 1.5;
}
.receipt-preview .receipt-meta .receipt-name{
  display: block;
  word-break: break-all;
  font-weight: 600;
}
.receipt-preview .receipt-meta .receipt-size{
  display: block;
  opacity: 0.65;
  font-size: 0.78rem;
}
.receipt-preview .receipt-clear{
  flex-shrink: 0;
  background: transparent;
  border: 1px solid rgba(var(--danger-rgb), 0.40);
  color: rgb(var(--danger-rgb));
  border-radius: 10px;
  padding: 6px 12px;
  font-size: 0.78rem;
  cursor: pointer;
  font-family: inherit;
}
.receipt-preview .receipt-clear:hover,
.receipt-preview .receipt-clear:focus-visible{
  background: rgba(var(--danger-rgb), 0.10);
  outline: none;
}
.glass-file-upload .file-error{
  display: block;
  margin-top: 6px;
  color: rgb(var(--danger-rgb));
  font-size: 0.8rem;
  line-height: 1.5;
}

/* Deposit-review modal: inline receipt block above the notes field. */
.dep-receipt-block{
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.dep-receipt-block .dep-receipt-label{
  font-size: 0.78rem;
  opacity: 0.65;
  font-weight: 600;
}
.dep-receipt-block .dep-receipt-img{
  display: block;
  max-width: 100%;
  max-height: 260px;
  border-radius: 12px;
  border: 1px solid rgba(255,255,255,0.10);
  background: rgba(0,0,0,0.20);
  object-fit: contain;
  cursor: zoom-in;
}
.dep-receipt-block .dep-receipt-pdf{
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px;
  border: 1px dashed rgba(255,255,255,0.18);
  border-radius: 12px;
  background: rgba(255,255,255,0.03);
  font-size: 0.85rem;
}
.dep-receipt-block .dep-receipt-pdf .dep-receipt-pdf-icon{ font-size: 1.6rem; }
.dep-receipt-block .dep-receipt-pdf a{
  color: rgb(var(--accent-rgb));
  text-decoration: underline;
  margin-inline-start: auto;
}
.dep-receipt-block .dep-receipt-open{
  align-self: flex-start;
  font-size: 0.78rem;
  color: rgb(var(--accent-rgb));
  text-decoration: underline;
}
html[data-theme="light"] .dep-receipt-block .dep-receipt-pdf{
  background: rgba(0,0,0,0.03);
  border-color: rgba(0,0,0,0.18);
}

/* Reject-reason validation — surfaced inline below the textarea. */
.dep-reason-warn{
  display: none;
  margin-top: 6px;
  font-size: 0.78rem;
  color: rgb(var(--danger-rgb));
}
.dep-reason-warn.show{ display: block; }

/* ═══════════════════════════════════════════════════════════════════════
   §62  TABLET BOTTOM SPACING (A-B)
   ═══════════════════════════════════════════════════════════════════════
   Between 768px and 1023px the bottom-nav is hidden (glass.css §41 only
   renders it ≤768px) but app.css §3 still pads the body by --mob-nav-h.
   Collapse the reserved strip to a normal page bottom on tablet. */
@media (min-width: 768px) and (max-width: 1023.98px){
  body{ padding-bottom: var(--sp-7, 32px); }
}

/* ═══════════════════════════════════════════════════════════════════════
   §63  MOBILE/TABLET TABLE COLLAPSE TWEAKS (A-C, H3.3, H3.15)
   ═══════════════════════════════════════════════════════════════════════
   - Defeat the generic .table[data-label] grid in app.css for tables that
     also collapse via the glass selectors here. The class .glass-tx-table
     wins because it carries `display: block` on <td>; app.css's
     `display: contents` on the same <td> would fight that rule. Belt: an
     explicit override below `.glass-tx-table td` ensures grid never wins.
   - Tablet (769–1023px): hide leaderboard's lowest-priority columns so
     the 9-column dashboard table stops overflowing the card.
   - Mobile 360–390: shrink stat-card values that can run to 4 digits + K
     so they don't overflow the card on narrow phones. */

@media (max-width: 767.98px){
  /* If a .glass-tx-table / .leaderboard / .table-wrap or .tbl-wrap table
     happens to ALSO carry the legacy .table class, force the glass rules
     to win over app.css §10's `display: grid` / `display: contents`. */
  .glass-tx-table.table tbody tr,
  .leaderboard.table tbody tr,
  .table-wrap > table.table:not(.no-collapse) tbody tr,
  .tbl-wrap > table.table:not(.no-collapse) tbody tr{
    display: block;
    grid-template-columns: none;
  }
  .glass-tx-table.table tbody td,
  .leaderboard.table tbody td,
  .table-wrap > table.table:not(.no-collapse) tbody td,
  .tbl-wrap > table.table:not(.no-collapse) tbody td{
    display: flex;
  }
  .glass-tx-table.table tbody td::before,
  .leaderboard.table tbody td::before,
  .table-wrap > table.table:not(.no-collapse) tbody td::before,
  .tbl-wrap > table.table:not(.no-collapse) tbody td::before{
    content: attr(data-label);
    font-size: 0.8rem;
    opacity: 0.65;
    font-weight: 600;
  }

  /* Truncate runaway notes so a single card with a 400-char note doesn't
     stretch and starve the rest. Keep break-word so the cell can wrap if
     it must. */
  .glass-tx-table td,
  .leaderboard td,
  .table-wrap > table td,
  .tbl-wrap > table td{
    overflow-wrap: anywhere;
    word-break: break-word;
    min-width: 0;
  }
  .glass-tx-table td bdi,
  .leaderboard td bdi{ overflow-wrap: anywhere; word-break: break-all; }
}

/* Mobile 360–390: stat-card numbers can overflow if both the K-suffix
   number and label hit the 2-column tile. Lower the font and let long
   words wrap. */
@media (max-width: 390px){
  .dashboard-stats .stat-val{
    font-size: 1.45rem;
    overflow-wrap: anywhere;
    word-break: break-word;
    min-width: 0;
  }
  .dashboard-stats .stat-lbl{
    font-size: 0.74rem;
    overflow-wrap: anywhere;
  }
  .dashboard-stats .glass-stat-card{
    min-width: 0;
    padding: 14px 12px;
  }
}

/* Tablet (768–1023px): the leaderboard fits 9 columns ONLY on ≥1024px.
   In between, hide the two lowest-priority columns (cost + wallet) — the
   key info (sales, profit, action) still shows. Sticky thead helps the
   operator scan a long list. */
@media (min-width: 768px) and (max-width: 1023.98px){
  .leaderboard{ font-size: 0.86rem; }
  .leaderboard thead{
    position: sticky;
    top: 0;
    background: rgba(20,22,32,0.85);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    z-index: 1;
  }
  html[data-theme="light"] .leaderboard thead{ background: rgba(255,255,255,0.92); }
  /* Hide هزینه عمده (col 6) + موجودی کیف پول (col 8). The dashboard table
     has 9 columns (1-based): #, name, level, child-count, sales, cost,
     profit, wallet, ops. Cost = nth-child(6); wallet = nth-child(8). */
  .leaderboard thead th:nth-child(6),
  .leaderboard tbody td:nth-child(6),
  .leaderboard thead th:nth-child(8),
  .leaderboard tbody td:nth-child(8){
    display: none;
  }
}
/* End of mobile/tablet table tweaks. */

/* ═══════════════════════════════════════════════════════════════════════
   §64  DISABLED FORM-CONTROL FAMILY (H1.5)
   ═══════════════════════════════════════════════════════════════════════
   The legacy .form-control and the cta-* buttons already have :disabled
   rules. Every other glass input/select/textarea/amount/stepper was
   silently identical to its enabled state — operators couldn't tell when
   an input was locked. One shared rule covers them all; component-level
   rules below it stay intact. */
.glass-input:disabled,
.glass-input[aria-disabled="true"],
.glass-select:disabled,
.glass-select[aria-disabled="true"],
.glass-textarea:disabled,
.glass-amount-input:disabled,
.glass-amount-input[readonly],
.glass-stepper .stepper-input:disabled,
.glass-input-group input:disabled,
.glass-input-group textarea:disabled,
.glass-input-group select:disabled,
.btn-glass:disabled,
.btn-glass[aria-disabled="true"]{
  opacity: 0.55;
  cursor: not-allowed;
  /* Soften the focus ring potential. */
  box-shadow: none;
}
/* Keep the inputs visibly "locked" — slightly dimmer background, no
   hover lift, no caret blink. Borders stay visible so the field shape
   reads. */
.glass-input:disabled,
.glass-select:disabled,
.glass-textarea:disabled,
.glass-amount-input:disabled,
.glass-stepper .stepper-input:disabled,
.glass-input-group input:disabled,
.glass-input-group textarea:disabled,
.glass-input-group select:disabled{
  background: rgba(255,255,255,0.02);
  color: var(--text-d, rgba(255,255,255,0.55));
  -webkit-text-fill-color: var(--text-d, rgba(255,255,255,0.55));
}
html[data-theme="light"] .glass-input:disabled,
html[data-theme="light"] .glass-select:disabled,
html[data-theme="light"] .glass-textarea:disabled,
html[data-theme="light"] .glass-amount-input:disabled,
html[data-theme="light"] .glass-stepper .stepper-input:disabled,
html[data-theme="light"] .glass-input-group input:disabled,
html[data-theme="light"] .glass-input-group textarea:disabled,
html[data-theme="light"] .glass-input-group select:disabled{
  background: rgba(0,0,0,0.025);
  color: rgba(0,0,0,0.45);
  -webkit-text-fill-color: rgba(0,0,0,0.45);
}
.btn-glass:disabled,
.btn-glass[aria-disabled="true"]{
  transform: none;
}
.btn-glass:disabled::before,
.btn-glass[aria-disabled="true"]::before{ opacity: 0; }

/* ═══════════════════════════════════════════════════════════════════════
   §65  KEYBOARD FOCUS-VISIBLE (H1.6)
   ═══════════════════════════════════════════════════════════════════════
   Several interactive surfaces had no :focus-visible style — keyboard
   users got no indication of where they were. One shared rule with the
   accent ring; per-surface rules above (auth, rep-menu, theme-FAB) win
   when more specific. */
.dur-btn:focus-visible,
.stepper-btn:focus-visible,
.glass-nav-item:focus-visible,
.glass-tab:focus-visible,
.queue-err-details summary:focus-visible,
.rep-action-primary:focus-visible,
.rep-action-secondary:focus-visible,
.bg-preset-tile:focus-visible,
.bg-preset-tile:has(input:focus-visible),
.glass-file-upload:focus-visible,
.glass-file-upload:has(input:focus-visible),
.url-copy-glass .btn-glass:focus-visible,
.modal-close:focus-visible,
[data-modal-close]:focus-visible,
.dep-receipt-block .dep-receipt-img:focus-visible,
.receipt-preview .receipt-clear:focus-visible,
[data-copy]:focus-visible{
  outline: 2px solid rgba(var(--accent-rgb), 0.65);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.18);
}
/* Visually-hidden radios inside the preset tiles should still be tab-able. */
.bg-preset-tile input[type="radio"]{
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  border: 0;
  pointer-events: auto;       /* H1.17: keyboard can reach the radio */
  opacity: 0;
}
/* End of §64–§65 — disabled + focus-visible. */

/* ═══════════════════════════════════════════════════════════════════════
   §66  RENEW PREVIEW SUMMARY (H4.8)
   ═══════════════════════════════════════════════════════════════════════
   Shared by my_subscription_detail.php + subscription_detail.php renew
   modals. The .glass-summary-dl host already lays out dt/dd as a grid;
   here we just give the renew variant a soft frame so the live total
   reads as a "receipt block" inside the modal. */
.renew-preview-dl{
  padding: 12px 14px;
  margin: 4px 0 12px;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  background: rgba(255,255,255,0.03);
  display: grid;
  grid-template-columns: minmax(45%, max-content) 1fr;
  gap: 6px 14px;
  font-size: 0.88rem;
}
html[data-theme="light"] .renew-preview-dl{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.08);
}
.renew-preview-dl dt{
  opacity: 0.7;
  font-size: 0.8rem;
  align-self: center;
}
.renew-preview-dl dd{
  margin: 0;
  font-variant-numeric: tabular-nums;
  align-self: center;
}
.renew-preview-dl dt.renew-total-label,
.renew-preview-dl dd.renew-total-value{
  border-top: 1px dashed rgba(255,255,255,0.08);
  padding-top: 8px;
  margin-top: 4px;
  opacity: 1;
  font-weight: 700;
}
html[data-theme="light"] .renew-preview-dl dt.renew-total-label,
html[data-theme="light"] .renew-preview-dl dd.renew-total-value{
  border-top-color: rgba(0,0,0,0.08);
}
.renew-preview-dl dd.renew-total-value{ color: rgb(var(--accent-rgb)); font-size: 1rem; }

/* ═══════════════════════════════════════════════════════════════════════
   §67  SETTINGS PAGE — JUMP NAV + BULK HINT + PW EYE (H6.6–H6.9)
   ═══════════════════════════════════════════════════════════════════════ */
.settings-jump{
  position: sticky;
  top: calc(var(--sp-2, 8px) + 8px);
  z-index: 30;
  display: flex;
  gap: 6px;
  margin-top: 14px;
  padding: 6px;
  overflow-x: auto;
  scrollbar-width: thin;
  border-radius: 14px;
  border: 1px solid rgba(255,255,255,0.08);
  background: rgba(20,22,32,0.65);
  backdrop-filter: blur(18px) saturate(1.3);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
}
html[data-theme="light"] .settings-jump{
  background: rgba(255,255,255,0.85);
  border-color: rgba(0,0,0,0.08);
}
.settings-jump-link{
  flex: 0 0 auto;
  padding: 8px 12px;
  border-radius: 10px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.06);
  transition: background 0.15s ease, border-color 0.15s ease;
}
.settings-jump-link:hover,
.settings-jump-link:focus-visible{
  background: rgba(var(--accent-rgb), 0.16);
  border-color: rgba(var(--accent-rgb), 0.40);
  outline: none;
}
html[data-theme="light"] .settings-jump-link{
  background: rgba(0,0,0,0.025);
  border-color: rgba(0,0,0,0.06);
}

.settings-bulk-hint{
  display: block;
  margin-top: 8px;
  padding: 8px 10px;
  font-size: 0.78rem;
  line-height: 1.65;
  background: rgba(var(--warn-rgb), 0.08);
  border: 1px solid rgba(var(--warn-rgb), 0.30);
  border-radius: 10px;
  color: var(--text);
  opacity: 1;
}
html[data-theme="light"] .settings-bulk-hint{
  background: rgba(var(--warn-rgb), 0.10);
  border-color: rgba(var(--warn-rgb), 0.35);
}

/* H6.7: eye toggle styling — app.js fills the SVG into the button on
   load and on click. Position inside .pw-wrap (inline-end so it sits
   on the right edge in RTL). */
.pw-wrap{ position: relative; }
.pw-wrap .pw-eye-btn{
  position: absolute;
  inset-inline-end: 8px;
  inset-block-start: 50%;
  transform: translateY(-50%);
  width: 28px;
  height: 28px;
  padding: 0;
  background: transparent;
  border: none;
  color: var(--text-m, var(--text));
  cursor: pointer;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.pw-wrap .pw-eye-btn:hover,
.pw-wrap .pw-eye-btn:focus-visible{
  background: rgba(255,255,255,0.08);
  color: var(--text);
  outline: none;
}
html[data-theme="light"] .pw-wrap .pw-eye-btn:hover,
html[data-theme="light"] .pw-wrap .pw-eye-btn:focus-visible{
  background: rgba(0,0,0,0.05);
}

/* H6.8: tiny unsaved-changes indicator next to each section's save
   button. The settings page is long; without a hint, operators forget
   they edited a field and navigate away. The indicator is rendered by
   the scoped IIFE in settings.php and is only visible while the form
   carries data-dirty="1". The save button gets an extra accent
   highlight in the same state. */
.settings-unsaved-indicator{
  /* UI-0235: keep the pill in the layout (reserved space) and toggle
     visibility/opacity instead of display:none → inline-flex, so the save
     row doesn't jump when the form becomes dirty. */
  display: inline-flex;
  visibility: hidden;
  opacity: 0;
  transition: opacity .15s ease;
  align-items: center;
  gap: 6px;
  margin-inline-start: 10px;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  color: rgb(252,211,77);
  background: rgba(var(--warn-rgb), 0.10);
  border: 1px solid rgba(var(--warn-rgb), 0.35);
}
form[data-dirty="1"] .settings-unsaved-indicator{ visibility: visible; opacity: 1; }
form[data-dirty="1"] .btn-glass.primary{
  box-shadow: 0 0 0 2px rgba(var(--warn-rgb), 0.45),
              0 6px 18px rgba(var(--warn-rgb), 0.15);
}
html[data-theme="light"] .settings-unsaved-indicator{
  color: rgb(146,64,14);
  background: rgba(var(--warn-rgb), 0.12);
}

/* ═══════════════════════════════════════════════════════════════════════
   §68  MOBILE BOTTOM-NAV LOGOUT BUTTON (H2.4)
   ═══════════════════════════════════════════════════════════════════════
   The logout form acts as a 5th grid cell inside .glass-bottom-nav
   .nav-items. The inner button reuses .glass-nav-item visuals but needs
   the button-default reset (background/border/font) and full-cell sizing
   so the icon + label center the same way as the <a> nav items. */
.glass-bottom-nav .glass-nav-form{
  display: flex;
  margin: 0;
  padding: 0;
  height: 100%;
}
.glass-nav-item.glass-nav-item-button{
  width: 100%;
  height: 100%;
  background: transparent;
  border: none;
  font-family: inherit;
}
.glass-nav-item.glass-nav-item-button:focus-visible{
  outline: 2px solid rgba(var(--accent-rgb), 0.55);
  outline-offset: -3px;
}

/* H2.4: align the desktop logout-form so it doesn't break the .nav
   flex/gap rhythm. The form itself sits inline; the button inherits
   its sibling .btn-glass.danger.sm visuals. */
.nav .logout-form{
  display: inline-flex;
  align-items: center;
}

/* ═══════════════════════════════════════════════════════════════════════
   §69  QUEUE — ERROR PREVIEW + EXPANDER (H3.6)
   ═══════════════════════════════════════════════════════════════════════
   Long last_error strings used to render as a wall of micro-text inside
   a 220px column. The PHP now emits an <details> with a short preview;
   expanding shows the full error inside a scrollable <pre>. */
.queue-err-cell{ max-width: 280px; }
.queue-err-cell code{ font-size: 0.78rem; word-break: break-word; }
.queue-err-details summary{
  cursor: pointer;
  list-style: none;
  display: inline-block;
  max-width: 100%;
  overflow-wrap: anywhere;
}
.queue-err-details summary::-webkit-details-marker{ display: none; }
.queue-err-details summary::marker{ content: ""; }
.queue-err-details[open] summary{ opacity: 0.7; }
.queue-err-details .queue-err-full{
  margin: 6px 0 0;
  padding: 8px 10px;
  font-size: 0.78rem;
  line-height: 1.55;
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 8px;
  max-height: 240px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, Consolas, monospace;
}
html[data-theme="light"] .queue-err-details .queue-err-full{
  background: rgba(0,0,0,0.04);
  border-color: rgba(0,0,0,0.08);
}
@media (max-width: 768px){
  /* On the card-collapsed mobile layout, allow the error cell to take
     full width and the inline-code preview to wrap freely. */
  .queue-err-cell{ max-width: 100%; min-width: 0; }
}

/* ═══════════════════════════════════════════════════════════════════════
   §70  UTILITY: .is-hidden + <mark> highlight (H1.12, H5.10)
   ═══════════════════════════════════════════════════════════════════════
   The tools-search filter in admin/search.php toggles .is-hidden on
   non-matching cards (assets/app.js IIFE). Without this rule the class
   does nothing visually. */
.is-hidden{ display: none !important; }

/* ═══════════════════════════════════════════════════════════════════════
   §71  REPRESENTATIVES — row helpers (H5.12)
   ═══════════════════════════════════════════════════════════════════════
   Promote a handful of repeated inline-style patterns from
   admin/representatives.php into the design system so the page stops
   leaking physical directional properties (margin-left, etc.). Only
   the safe, identifiable patterns are migrated; the rest stay inline
   to keep the diff small and the visual output unchanged. */
.rep-row-logo{
  width: 28px;
  height: 28px;
  border-radius: 6px;
  object-fit: cover;
  vertical-align: middle;
  /* H5.12: physical margin-left → logical margin-inline-end so the
     gap between the logo and the name sits on the trailing inline
     side in either locale. In Persian RTL the logo precedes the
     name; the gap belongs after it (visually to its left). */
  margin-inline-end: 6px;
}
.rep-row-name{ font-weight: 700; }
.rep-row-username{
  opacity: 0.6;
  font-size: 0.78rem;
  font-family: ui-monospace, monospace;
}
.rep-row-pagination{
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 14px;
  padding-inline: 8px;
  flex-wrap: wrap;
  gap: 8px;
}
.rep-row-pagination .pager-meta{
  opacity: 0.65;
  font-size: 0.85rem;
}
.rep-row-pagination .pager-nav{
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  align-items: center;
}
/* H6.5: active page chip — mirrors .btn-glass.neutral.sm visually but
   sits in a non-interactive <span> when there is no link to follow. */
.rep-row-pagination .pager-nav .pager-current{
  pointer-events: none;
  background: rgba(var(--accent-rgb), 0.18);
  border-color: rgba(var(--accent-rgb), 0.45);
  color: var(--text);
}
html[data-theme="light"] .rep-row-pagination .pager-nav .pager-current{
  background: rgba(var(--accent-rgb), 0.14);
  border-color: rgba(var(--accent-rgb), 0.40);
}
.rep-row-pagination .pager-nav .pager-ellipsis{
  padding: 0 6px;
  opacity: 0.55;
  align-self: center;
}

/* Search-results match highlighting. <mark> default browser styling is
   yellow background + black text — fine on light theme but unreadable
   on dark. Token-aware variant keeps the existing glass identity. */
.search-results mark,
mark.search-hit{
  background: rgba(var(--warn-rgb), 0.30);
  color: inherit;
  padding: 0 2px;
  border-radius: 4px;
  font-weight: 700;
}
html[data-theme="light"] .search-results mark,
html[data-theme="light"] mark.search-hit{
  background: rgba(var(--warn-rgb), 0.45);
}

/* Row-as-link affordance for the search result rows. The PHP wraps a
   data-href onto each <tr>; a tiny JS handler navigates on click. */
.glass-tx-table tr[data-href]{ cursor: pointer; }
.glass-tx-table tr[data-href]:hover td{ background: rgba(var(--accent-rgb), 0.06); }
html[data-theme="light"] .glass-tx-table tr[data-href]:hover td{
  background: rgba(var(--accent-rgb), 0.08);
}

/* ═══════════════════════════════════════════════════════════════════════
   §66  BACKDROP-FILTER REPAINT-FLICKER FIX
   ═══════════════════════════════════════════════════════════════════════
   Symptom (reported on dashboard / buy / deposits and any page with glass
   cards): a bright/white halo flashes over part of a card as the pointer
   moves — a "blinking glow" that isn't in any :hover or JS handler.

   Cause: every glass surface samples the page background through
   `backdrop-filter: blur()`, and that background is a position:fixed blob
   layer (`body:not(.has-app-bg)::before`, §50). On each repaint — pointer
   hit-testing, the button/badge :hover transitions, the infinite
   `.badge-glass.active` pulse — Chromium re-rasterizes the backdrop buffer
   and it momentarily renders too bright before settling. There is no
   styled white glow; this is purely a compositing artifact.

   Fix: pin the sampled background layer AND the glass surfaces to their
   own stable GPU compositing layer with translateZ(0). The cached backing
   store stops the per-repaint re-rasterization, so the blur stays steady.
   This is safe and changes nothing visual: `backdrop-filter` ALREADY makes
   each of these a stacking context + containing block, so translateZ(0)
   adds no new stacking/containing-block behavior and (in current engines)
   does not disable the blur — it only promotes a persistent layer.
   ═══════════════════════════════════════════════════════════════════════ */
/* NOTE: only the fixed-background *pseudo-elements* are promoted, never
   <body> itself — a transform on <body> would turn it into the containing
   block for every position:fixed descendant (modals, bottom-nav, theme-FAB)
   and break their viewport pinning. */
body:not(.has-app-bg)::before,
.glass-app-bg::before{
  transform: translateZ(0);
  will-change: transform;
}
.glass-card,
.glass-stat-card,
.glass-page-head,
.glass-info-bar,
.glass-form-section,
.glass-method-card,
.glass-balance-card,
.glass-deposit-row,
.glass-rep-card,
.filters-glass{
  transform: translateZ(0);
}
/* Engines without backdrop-filter already collapse these surfaces to opaque
   tints (§52), so the layer-promotion is harmless there too. Respect the
   motion/transparency preference symmetry: nothing here animates, so no
   reduced-motion carve-out is required. */

/* ═══════════════════════════════════════════════════════════════════════
   §67  GLASS CHECKBOX / RADIO  +  GLASS RANGE
   ═══════════════════════════════════════════════════════════════════════
   Drop-in styled replacements for raw browser checkboxes, radios and range
   sliders so they match the glass input family. OPT-IN via class
   (.glass-check / .glass-range) — never a bare input[type=...] rule — so the
   pre-existing visually-hidden custom toggles (.auto-username-check, the
   .bg-preset-tile radios) keep their own treatment untouched. All colours
   come from the shared tokens (--input-bg / --border / --border-strong /
   --accent / --accent-rgb), so both themes are handled automatically.
   The native control's name/value/checked/JS behaviour is unchanged — this
   only repaints the box. */
/* Selector is `input.glass-check` (not bare `.glass-check`) on purpose: it
   raises specificity to (0,1,1) so the fixed 19×19 box always beats the
   generic `.glass-input-group input { width:100%; padding:10px 12px }` rule
   (also 0,1,1, but earlier in the file). Without this, a .glass-check placed
   inside a .glass-input-group inherits width:100% + padding and — because
   appearance:none lets width apply — stretches to the full container width,
   breaking the row. `padding:0` neutralises the same inherited padding. */
input.glass-check{
  appearance: none; -webkit-appearance: none;
  flex: 0 0 auto;
  box-sizing: border-box;
  width: 19px; height: 19px;
  min-width: 19px;            /* belt-and-braces: never shrink/stretch in a flex row */
  margin: 0;
  padding: 0;                 /* override .glass-input-group input's 10px 12px */
  display: inline-grid;
  place-content: center;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  vertical-align: middle;
  transition: background var(--dur-1, 120ms) var(--ease, ease),
              border-color var(--dur-1, 120ms) var(--ease, ease),
              box-shadow var(--dur-1, 120ms) var(--ease, ease);
}
.glass-check[type="radio"]{ border-radius: 50%; }

/* The check/dot is drawn in CSS (no SVG asset). Hidden until :checked via a
   scale transform so the toggle animates with the rest of the UI. */
.glass-check::after{
  content: "";
  transform: scale(0);
  transition: transform var(--dur-1, 120ms) var(--ease, ease);
}
.glass-check[type="checkbox"]::after{
  width: 5px; height: 9px;
  border: solid #fff;            /* tick reads on the accent fill in both themes */
  border-width: 0 2px 2px 0;
  border-radius: 1px;
  margin-block-start: -2px;      /* optical centring after the 45° rotation */
  transform: rotate(45deg) scale(0);
}
.glass-check[type="checkbox"]:checked::after{ transform: rotate(45deg) scale(1); }
.glass-check[type="radio"]::after{
  width: 9px; height: 9px;
  border-radius: 50%;
  background: #fff;
}
.glass-check[type="radio"]:checked::after{ transform: scale(1); }

.glass-check:checked{
  background: var(--accent);
  border-color: var(--accent);
}
.glass-check:not(:checked):hover{ border-color: var(--border-strong); }
.glass-check:focus-visible{
  outline: none;
  border-color: var(--border-strong);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.18);
}
.glass-check:disabled{ opacity: 0.5; cursor: not-allowed; }

/* Glass range slider — track painted by the slider pseudo-elements, thumb is
   an accent pill. Vendor-prefixed track/thumb must stay in separate rules
   (a single grouped selector is dropped wholesale by every engine). */
.glass-range{
  -webkit-appearance: none; appearance: none;
  width: 100%;
  height: 18px;                  /* room for the thumb; track drawn below */
  margin: 4px 0;
  background: transparent;
  cursor: pointer;
}
.glass-range::-webkit-slider-runnable-track{
  height: 6px; border-radius: 999px;
  background: var(--input-bg);
  border: 1px solid var(--border);
}
.glass-range::-moz-range-track{
  height: 6px; border-radius: 999px;
  background: var(--input-bg);
  border: 1px solid var(--border);
}
.glass-range::-webkit-slider-thumb{
  -webkit-appearance: none; appearance: none;
  margin-top: -7px;              /* centre 18px thumb on the 8px track box */
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--accent);
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0,0,0,0.35);
  cursor: pointer;
}
.glass-range::-moz-range-thumb{
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--accent);
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0,0,0,0.35);
  cursor: pointer;
}
.glass-range:focus-visible{ outline: none; }
.glass-range:focus-visible::-webkit-slider-thumb{
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.25), 0 1px 4px rgba(0,0,0,0.35);
}
.glass-range:focus-visible::-moz-range-thumb{
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.25), 0 1px 4px rgba(0,0,0,0.35);
}
