/* SmartBot.MN client area.
   The design is a soft "glass over neumorphism" system: translucent blurred
   cards floating on a tinted gradient ground, with controls that are either
   pressed into the surface (inputs, segmented tracks) or raised out of it
   (buttons). The tokens below are the whole vocabulary; nothing should
   hard-code a colour or a shadow that is not here. */

:root {
  /* Ground and ink */
  --bg: #eceff7;
  --ink: #23243a;
  --ink-soft: #4d5070;
  --ink-muted: #6e7191;
  --ink-faint: #8a8ea8;
  --placeholder: #9a9db5;

  /* Brand */
  --violet: #7c5cff;
  --violet-deep: #6a4ee6;
  --violet-deeper: #4f36c7;
  --violet-light: #8f7bff;
  --blue: #5b8cff;
  --brand: linear-gradient(135deg, #7c5cff, #5b8cff);
  --brand-soft: linear-gradient(135deg, #8f7bff, #5b8cff);

  /* Status accents */
  --green: #48c78e;
  --green-ink: #1c7a4c;
  --amber: #ffb347;
  --amber-ink: #9a5b00;
  --red: #ff7a90;
  --red-ink: #a12b2b;

  /* Glass surfaces */
  --glass: rgba(255, 255, 255, 0.55);
  --glass-strong: rgba(255, 255, 255, 0.75);
  --glass-edge: rgba(255, 255, 255, 0.8);
  --glass-blur: blur(18px);
  --glass-blur-lg: blur(22px);

  /* Neumorphic surfaces */
  --sunken: rgba(232, 236, 246, 0.9);
  --raised: linear-gradient(145deg, #f7f8fc, #e6e9f3);

  /* Elevation */
  --shadow-card: 0 10px 30px rgba(90, 100, 150, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.95);
  --shadow-panel: 0 16px 40px rgba(90, 100, 150, 0.14), inset 0 1px 0 rgba(255, 255, 255, 0.95);
  --shadow-modal: 0 24px 60px rgba(90, 100, 150, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.95);
  --shadow-in: inset 3px 3px 7px rgba(120, 130, 170, 0.22), inset -3px -3px 7px rgba(255, 255, 255, 0.95);
  --shadow-out: 4px 4px 10px rgba(120, 130, 170, 0.22), -4px -4px 10px rgba(255, 255, 255, 0.9);
  --shadow-out-sm: 3px 3px 8px rgba(120, 130, 170, 0.22), -3px -3px 8px rgba(255, 255, 255, 0.9);
  --shadow-brand: 0 10px 24px rgba(108, 92, 255, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.35);

  /* Radii */
  --r-xl: 28px;
  --r-lg: 22px;
  --r-md: 16px;
  --r-sm: 14px;
  --r-xs: 11px;

  --mono: "Ubuntu Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}

* { box-sizing: border-box; }

html, body { margin: 0; min-height: 100%; }

body {
  font-family: "Ubuntu", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, sans-serif;
  color: var(--ink);
  background: var(--bg);
  background-image:
    radial-gradient(640px 520px at 92% 4%, rgba(124, 92, 255, 0.32), transparent 70%),
    radial-gradient(700px 560px at 2% 96%, rgba(91, 140, 255, 0.28), transparent 70%),
    radial-gradient(520px 420px at 55% 60%, rgba(255, 179, 71, 0.18), transparent 70%),
    radial-gradient(420px 380px at 20% 30%, rgba(72, 199, 142, 0.14), transparent 70%);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--violet-deep); text-decoration: none; font-weight: 600; }
a:hover { color: var(--violet-deeper); text-decoration: underline; }

input, textarea, select, button { font-family: inherit; }
::placeholder { color: var(--placeholder); }

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

[hidden] { display: none !important; }

@keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
@keyframes fade-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* ---------------------------------------------------------------- surfaces */

.card {
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
}

.card-head {
  padding: 14px 18px;
  border-bottom: 1px solid var(--glass-edge);
  box-shadow: 0 1px 0 rgba(120, 130, 170, 0.12);
  font-weight: 700;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.card-body { padding: 16px 18px; display: flex; flex-direction: column; gap: 14px; }

/* ----------------------------------------------------------------- buttons */

.btn {
  padding: 9px 15px;
  font-size: 13px;
  font-weight: 700;
  color: #3c3f5c;
  background: var(--raised);
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: 12px;
  box-shadow: var(--shadow-out);
  cursor: pointer;
  white-space: nowrap;
}
.btn:hover { color: var(--violet-deep); }
.btn:active { transform: translateY(1px); box-shadow: var(--shadow-out-sm); }

.btn-primary {
  color: #fff;
  background: var(--brand);
  border: 0;
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-brand);
  padding: 10px 16px;
}
.btn-primary:hover { color: #fff; filter: brightness(1.06); }
.btn-primary:active { transform: translateY(1px); box-shadow: 0 3px 10px rgba(108, 92, 255, 0.3); }

/* An offer rather than the page's one order — four of them stand side by side
   under the price list. The brand's colour, quietly: a tint, and the deeper
   violet for the words, which is what keeps them above 4.5:1 on it. */
.btn-tonal {
  padding: 10px 16px;
  font-size: 13px;
  font-weight: 700;
  color: var(--violet-deeper);
  background: rgba(124, 92, 255, 0.12);
  border: 1px solid rgba(124, 92, 255, 0.3);
  border-radius: var(--r-sm);
  cursor: pointer;
  white-space: nowrap;
}
.btn-tonal:hover { background: rgba(124, 92, 255, 0.2); }
.btn-tonal:active { transform: translateY(1px); }

.btn-lg { padding: 12px 18px; font-size: 15px; }
.btn-sm { padding: 6px 11px; font-size: 12px; }
.btn-block { width: 100%; }

.btn:disabled, .btn-primary:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

.btn-ghost {
  background: none;
  border: 0;
  box-shadow: none;
  color: var(--violet-deep);
  cursor: pointer;
  font-weight: 700;
  padding: 6px 8px;
  align-self: flex-start;
}

/* A link that acts as a button — a checkout page, a wallet deeplink, a
   one-time connect link. It has to be a real anchor so the customer's own
   click opens it and no popup blocker gets involved. */
a.btn, a.btn-primary {
  display: inline-block;
  text-align: center;
  text-decoration: none;
  max-width: 100%;
}
a.btn:hover, a.btn-primary:hover { text-decoration: none; }
a.btn-block { display: block; }

/* Segmented control: the selected segment lifts out of a sunken track. */
.seg {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 4px;
  border-radius: var(--r-sm);
  background: rgba(228, 232, 242, 0.9);
  box-shadow: var(--shadow-in);
  min-width: 0;
}
.seg > button {
  white-space: nowrap;
  padding: 7px 13px;
  font-size: 13px;
  font-weight: 700;
  border-radius: 10px;
  border: 0;
  cursor: pointer;
  background: transparent;
  color: #5b5e7a;
  box-shadow: none;
}
.seg > button:hover { color: var(--violet-deep); }
.seg > button[aria-selected="true"] {
  background: linear-gradient(145deg, #ffffff, #eef0f8);
  color: var(--violet-deep);
  box-shadow: var(--shadow-out-sm);
}
.seg > button:disabled { opacity: 0.45; cursor: not-allowed; }
/* The four cycles a chatbot is paid by: the month and the three months share
   a row, and the two that say what they save — six months and the year —
   each have a row to themselves, so no label has to break. */
.seg.seg-cycles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.seg-cycles > button { padding: 7px 4px; text-align: center; white-space: normal; line-height: 1.2; }
.seg-cycles > button:nth-child(n+3) { grid-column: 1 / -1; }
/* The pay dialog's three ways to pay sit on one row, sharing it equally; on a
   very narrow phone a label wraps inside its own tab rather than pushing a tab
   onto a second row. */
.modal .seg { flex-wrap: nowrap; }
.modal .seg > button { flex: 1 1 auto; min-width: 0; padding: 7px 6px; text-align: center; }
@media (max-width: 420px) {
  .modal .seg > button { white-space: normal; line-height: 1.2; font-size: 12px; padding: 6px 4px; }
}

/* Inviting a friend, on the dashboard and the profile page: the link beside
   the button that copies it, what the invitations have come to as three small
   tiles, and the rewards as a list. The tiles are the ordinary labelled value
   (.svc-field) given a surface; they share a row while there is room for all
   three and wrap, each still filling its row, when there is not. */
.ref-link { display: flex; gap: 8px; align-items: stretch; }
.ref-link .input { flex: 1; min-width: 0; }
.ref-link .btn { flex: none; }
.ref-figures { display: flex; flex-wrap: wrap; gap: 10px; }
.ref-figures .svc-field {
  flex: 1 1 120px;
  padding: 10px 14px;
  border-radius: var(--r-sm);
  background: var(--glass);
  border: 1px solid var(--glass-edge);
}
.ref-figures .svc-field .v { font-size: 18px; }
.ref-recent { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }

/* ------------------------------------------------------------------ fields */

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-soft);
}
.field > input, .field > select, .field > textarea, .input {
  font-size: 14px;
  padding: 11px 14px;
  border: 1px solid rgba(255, 255, 255, 0.9);
  outline: none;
  border-radius: 13px;
  background: var(--sunken);
  box-shadow: var(--shadow-in);
  color: var(--ink);
  font-weight: 500;
  width: 100%;
}
.field > textarea { resize: vertical; }

/* Toggle switch: a sunken track with a raised knob. */
.switch {
  position: relative;
  width: 50px;
  height: 28px;
  flex: none;
  border: 0;
  border-radius: 999px;
  background: rgba(228, 232, 242, 0.95);
  cursor: pointer;
  transition: background 0.18s ease;
}
.switch[aria-pressed="true"] {
  background: var(--brand);
  box-shadow: inset 2px 2px 5px rgba(50, 30, 140, 0.35);
}
.switch::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 2px 6px rgba(90, 100, 150, 0.35);
  transition: left 0.18s ease;
}
.switch[aria-pressed="true"]::after { left: 25px; }
.switch:disabled { cursor: not-allowed; opacity: 0.75; }

/* -------------------------------------------------------------------- tags */

.tag {
  font-size: 12px;
  font-weight: 700;
  padding: 3px 10px;
  border-radius: 9px;
  white-space: nowrap;
}
.tag-paid { color: var(--green-ink); background: rgba(72, 199, 142, 0.22); border: 1px solid rgba(72, 199, 142, 0.45); }
.tag-unpaid { color: var(--amber-ink); background: rgba(255, 178, 58, 0.25); border: 1px solid rgba(255, 178, 58, 0.55); }
.tag-overdue { color: var(--red-ink); background: rgba(255, 99, 99, 0.2); border: 1px solid rgba(255, 99, 99, 0.45); }
/* Part of it paid, the rest owed: neither of the two above. */
.tag-part { color: #1f5fa8; background: rgba(86, 156, 240, 0.2); border: 1px solid rgba(86, 156, 240, 0.5); }
.tag-neutral { color: var(--ink-soft); background: rgba(255, 255, 255, 0.7); border: 1px solid rgba(255, 255, 255, 0.95); }

.pill {
  font-size: 12px;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: 9px;
  background: rgba(255, 255, 255, 0.7);
  border: 1px solid rgba(255, 255, 255, 0.95);
  color: var(--ink-soft);
}
.pill-ai { background: rgba(124, 92, 255, 0.16); border-color: rgba(124, 92, 255, 0.35); color: var(--violet-deep); }

.mono { font-family: var(--mono); font-weight: 400; letter-spacing: -0.01em; }

/* ------------------------------------------------------------------- badge */

.brand-mark {
  border-radius: 14px;
  background: var(--brand-soft);
  box-shadow: 0 8px 18px rgba(108, 92, 255, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.4);
  display: grid;
  place-items: center;
  color: #fff;
  font-weight: 700;
  flex: none;
}

/* ================================================================== auth == */

.auth-wrap { min-height: 100vh; display: grid; place-items: center; padding: 24px; }

.auth-card {
  width: min(880px, 100%);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  background: rgba(255, 255, 255, 0.5);
  backdrop-filter: var(--glass-blur-lg);
  -webkit-backdrop-filter: var(--glass-blur-lg);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-modal);
  overflow: hidden;
}

.auth-aside {
  padding: 40px 36px;
  display: flex;
  flex-direction: column;
  gap: 22px;
  background: linear-gradient(160deg, rgba(124, 92, 255, 0.16), rgba(91, 140, 255, 0.08));
  border-right: 1px solid rgba(255, 255, 255, 0.7);
}
.auth-form { padding: 40px 36px; display: flex; flex-direction: column; gap: 14px; justify-content: center; }
.auth-form h1 { margin: 0 0 6px; font-size: 24px; font-weight: 700; letter-spacing: -0.01em; }
.auth-lede { font-size: 22px; font-weight: 700; line-height: 1.25; letter-spacing: -0.01em; }
.auth-points { display: flex; flex-direction: column; gap: 10px; font-size: 14px; color: var(--ink-soft); }
.auth-points > div { display: flex; gap: 10px; align-items: center; }
.auth-points span {
  width: 26px; height: 26px; border-radius: 9px; flex: none;
  display: grid; place-items: center; font-weight: 700; font-size: 13px;
}
.robot { animation: float 5s ease-in-out infinite; align-self: center; margin: 6px 0; }

.auth-links { display: flex; justify-content: space-between; font-size: 13px; }

.form-error {
  font-size: 13px;
  font-weight: 700;
  color: var(--red-ink);
  background: rgba(255, 99, 99, 0.15);
  border: 1px solid rgba(255, 99, 99, 0.4);
  border-radius: 12px;
  padding: 9px 12px;
}
/* What a form says when it has done what was asked (a link sent, a password
   set). Wraps anywhere: it can carry an email address. */
.form-ok {
  font-size: 13px;
  font-weight: 700;
  color: #1c7a4c;
  background: rgba(72, 199, 142, 0.16);
  border: 1px solid rgba(72, 199, 142, 0.45);
  border-radius: 12px;
  padding: 9px 12px;
  overflow-wrap: anywhere;
}

/* Иргэн or Байгууллага: two cards side by side, stacking when the column is
   too narrow for both. */
.choice-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr)); gap: 10px; }
.choice {
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-align: left;
  padding: 12px 14px;
  cursor: pointer;
  border-radius: var(--r-sm);
  border: 1px solid rgba(255, 255, 255, 0.9);
  background: var(--raised);
  box-shadow: var(--shadow-out-sm);
  color: var(--ink-soft);
  min-width: 0;
}
.choice[aria-pressed="true"] {
  border-color: rgba(124, 92, 255, 0.6);
  box-shadow: 0 0 0 3px rgba(124, 92, 255, 0.18), var(--shadow-out-sm);
  color: var(--violet-deep);
}
.choice-label { font-size: 15px; font-weight: 700; }
.choice-note { font-size: 11.5px; font-weight: 500; color: var(--ink-muted); line-height: 1.45; }

/* The register-number lookup and the customer's confirmation of the name it
   comes back with. */
.check { display: flex; flex-direction: column; gap: 10px; }
.check-row { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; }
.check-row .field { flex: 1 1 200px; min-width: 0; }
.check-row .btn { flex: none; padding: 11px 16px; }
.check-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 14px;
  border-radius: var(--r-sm);
  background: rgba(124, 92, 255, 0.1);
  border: 1px solid rgba(124, 92, 255, 0.28);
}
.check-card.is-ok { background: rgba(72, 199, 142, 0.14); border-color: rgba(72, 199, 142, 0.4); }
.check-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--ink-muted); font-weight: 700; }
.check-name { font-size: 15px; font-weight: 700; overflow-wrap: anywhere; }
.check-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 2px; }
.check-note { font-size: 12px; font-weight: 600; color: var(--ink-soft); line-height: 1.5; }
.check-note.is-warn {
  color: var(--amber-ink);
  background: rgba(255, 178, 58, 0.18);
  border: 1px solid rgba(255, 178, 58, 0.45);
  border-radius: 12px;
  padding: 9px 12px;
}

/* The code step of registration: the code texted to the customer's phone,
   typed into one field. The digits are set large and apart, the way they read
   in the message. Letter-spacing trails the last digit as well, so the indent
   puts the same space before the first and the four sit centred. */
.field > input.code-input {
  text-align: center;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.45em;
  text-indent: 0.45em;
  font-variant-numeric: tabular-nums;
}
/* Under the button: the way back to the form, and the resend. The two share a
   row and wrap on a phone too narrow for both. The resend carries a countdown
   while it cannot be pressed, in figures that do not shift as they change. */
.code-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 6px 12px;
  font-size: 13px;
}
.code-actions .btn-ghost {
  align-self: auto;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.code-actions .btn-ghost:disabled { opacity: 0.55; cursor: not-allowed; }

/* ================================================================= shell == */

.app { min-height: 100vh; display: flex; flex-direction: column; }
.shell, .app { max-width: 100%; }

.topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-bottom: 1px solid var(--glass-edge);
  box-shadow: 0 8px 24px rgba(90, 100, 150, 0.1);
  flex-wrap: wrap;
}
.topbar .wordmark { margin-right: auto; }

.wordmark { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 17px; }
.wordmark .brand-mark { width: 30px; height: 30px; border-radius: 10px; font-size: 15px; }
/* The logo itself (smartbot.mn's): it brings its own colour and shape (``brandMark`` in app.js). */
.brand-mark.is-logo { background: none; box-shadow: none; border-radius: 50%; }
.brand-mark.is-logo img { display: block; width: 100%; height: 100%; }

.shell { display: flex; flex: 1; align-items: stretch; }

.sidebar {
  width: 240px;
  flex: none;
  display: flex;
  flex-direction: column;
  margin: 18px 0 18px 18px;
  position: sticky;
  top: 18px;
  align-self: flex-start;
  min-height: calc(100vh - 36px);
  background: rgba(255, 255, 255, 0.5);
  backdrop-filter: var(--glass-blur-lg);
  -webkit-backdrop-filter: var(--glass-blur-lg);
  border: 1px solid var(--glass-edge);
  border-radius: 26px;
  box-shadow: var(--shadow-panel);
}
.sidebar-head { margin: 14px 14px 6px; padding: 12px; display: flex; align-items: center; gap: 10px; }
.sidebar-head .brand-mark { width: 38px; height: 38px; border-radius: 13px; font-size: 17px; }
.sidebar-title { font-weight: 700; font-size: 16px; letter-spacing: -0.01em; }
.sidebar-sub { font-size: 11px; color: var(--ink-muted); }

.side-nav { display: flex; flex-direction: column; gap: 4px; padding: 6px 12px; }
.side-nav button {
  display: flex;
  align-items: center;
  gap: 10px;
  text-align: left;
  padding: 10px 12px;
  font-size: 14px;
  font-weight: 700;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  cursor: pointer;
  background: transparent;
  color: var(--ink-soft);
}
.side-nav button:hover { color: var(--violet-deep); }
.side-nav button[aria-current="page"] {
  background: linear-gradient(145deg, #ffffff, #eef0f8);
  border-color: rgba(255, 255, 255, 0.95);
  color: var(--violet-deep);
  box-shadow: var(--shadow-out);
}
.nav-label { flex: 1; }
.nav-badge {
  font-size: 11px;
  padding: 1px 8px;
  border-radius: 9px;
  background: linear-gradient(135deg, #ff7a90, #ff9a62);
  box-shadow: 0 3px 8px rgba(255, 120, 120, 0.4);
  color: #fff;
}

.nav-dot-dashboard { background: linear-gradient(135deg, #7c5cff, #5b8cff); }
.nav-dot-profile { background: linear-gradient(135deg, #a98bf5, #7360f2); }

.sidebar-foot {
  margin: auto 12px 0;
  padding: 14px 12px;
  border-top: 1px solid var(--glass-edge);
  box-shadow: 0 -1px 0 rgba(120, 130, 170, 0.12);
  display: flex;
  align-items: center;
  gap: 10px;
}
.avatar {
  width: 36px; height: 36px; border-radius: 50%; flex: none;
  background: var(--raised);
  box-shadow: var(--shadow-out);
  display: grid; place-items: center;
  color: var(--violet-deep); font-weight: 700; font-size: 13px;
}
.sidebar-user { flex: 1; min-width: 0; }
.sidebar-user .nm { font-size: 13px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sidebar-user .co { font-size: 11px; color: var(--ink-muted); }

main.content {
  flex: 1;
  min-width: 0;
  max-width: 100%;
  padding: 26px clamp(14px, 3vw, 40px) 60px;
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.crumb { font-size: 12px; color: var(--ink-muted); font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; }
.page-head h1 { margin: 2px 0 0; font-size: 28px; font-weight: 700; letter-spacing: -0.02em; }

.balance-chip {
  font-size: 13px;
  color: var(--ink-soft);
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 6px 6px 6px 14px;
  border-radius: var(--r-md);
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-edge);
  box-shadow: 0 8px 20px rgba(90, 100, 150, 0.1);
}
.balance-chip .amt {
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 400;
  padding: 6px 12px;
  background: var(--sunken);
  border-radius: var(--r-xs);
  box-shadow: var(--shadow-in);
}

/* What the page says back after something was asked of it. It keeps to the top
   of the screen while the page scrolls under it: the button that was pressed
   may be a screen or two down — «Хадгалах» is, on a phone — and an answer
   printed at the head of the page would otherwise be given to nobody. So its
   ground is all but solid: it has to be read over whatever passes beneath. */
.toast {
  position: sticky;
  top: 14px;
  z-index: 4;
  padding: 12px 16px;
  background: rgba(238, 234, 255, 0.94);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(124, 92, 255, 0.35);
  border-radius: var(--r-sm);
  box-shadow: 0 8px 22px rgba(90, 100, 150, 0.14);
  font-size: 14px;
  font-weight: 600;
  color: #3b2a90;
  display: flex;
  justify-content: space-between;
  gap: 12px;
  animation: fade-in 0.2s ease;
}
.toast.is-error { background: rgba(255, 236, 236, 0.95); border-color: rgba(255, 99, 99, 0.4); color: var(--red-ink); }
.toast button { border: 0; background: none; cursor: pointer; color: inherit; font-weight: 700; }
.toast .spin { margin-right: 8px; }

/* A standing state the customer has to act on — service paused for payment, a
   channel still to connect. Calmer than an error, and it has no dismiss: it
   stays until the state behind it changes. */
.notice {
  display: flex;
  gap: 14px;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  padding: 14px 18px;
  border-radius: var(--r-md);
  border: 1px solid rgba(255, 255, 255, 0.9);
  background: var(--glass-strong);
  box-shadow: var(--shadow-card);
}
.notice-body { flex: 1 1 260px; min-width: 0; }
.notice-title { font-weight: 700; font-size: 14px; }
.notice-text { font-size: 13px; color: var(--ink-soft); line-height: 1.55; margin-top: 3px; overflow-wrap: anywhere; }
.notice-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.notice-warn { background: rgba(255, 178, 58, 0.18); border-color: rgba(255, 178, 58, 0.5); }
.notice-warn .notice-title { color: var(--amber-ink); }
.notice-info { background: rgba(124, 92, 255, 0.1); border-color: rgba(124, 92, 255, 0.3); }
.notice-info .notice-title { color: var(--violet-deep); }

.page { display: flex; flex-direction: column; gap: 22px; animation: fade-in 0.2s ease; }

/* ============================================================== dashboard = */

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr)); gap: 16px; }
.stat { padding: 18px 20px; position: relative; overflow: hidden; border-radius: 20px; }
/* A figure that opens its page (app.js STAT_GOES): a button dressed as the card it was. */
.stat.is-link { display: flex; flex-direction: column; justify-content: flex-start; width: 100%;
  text-align: left; font: inherit; color: inherit; cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease; }
.stat.is-link:hover { transform: translateY(-2px); box-shadow: var(--shadow-modal); }
.stat.is-link:focus-visible { outline: 2px solid var(--violet); outline-offset: 2px; }
.stat .blob { position: absolute; right: -14px; top: -14px; width: 64px; height: 64px; border-radius: 50%; opacity: 0.35; }
.stat .label { font-size: 12px; color: var(--ink-muted); font-weight: 600; }
.stat .value {
  font-size: 24px;
  font-weight: 700;
  margin-top: 6px;
  font-family: var(--mono);
  letter-spacing: -0.02em;
  overflow-wrap: anywhere;
}
.stat .sub { font-size: 12px; color: var(--ink-faint); margin-top: 2px; overflow-wrap: anywhere; }
.tone-green { background: var(--green); }
.tone-amber { background: var(--amber); }
.tone-violet { background: var(--violet); }
.tone-blue { background: var(--blue); }

.two-col { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); gap: 20px; align-items: start; }

.row-item {
  padding: 14px 18px;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  border-bottom: 1px solid rgba(255, 255, 255, 0.65);
}
.row-item:last-child { border-bottom: 0; }
.row-main { flex: 1; min-width: 160px; }
.row-id { font-weight: 700; font-size: 14px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.row-sub { font-size: 12px; color: var(--ink-muted); margin-top: 2px; }
/* Which chatbot an invoice is for: the first thing a customer with several looks for. */
.row-sub.row-bot { color: var(--ink); font-weight: 500; }
.row-amount { font-family: var(--mono); font-weight: 400; font-size: 15px; }
.row-actions { display: flex; gap: 8px; }

.empty { padding: 22px 18px; display: flex; gap: 16px; align-items: center; font-size: 13px; color: var(--ink-soft); }
.empty b { font-size: 14px; color: var(--ink); }

.bot-card {
  padding: 16px;
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.9);
  box-shadow: var(--shadow-card);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.bot-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; }
.bot-name { font-weight: 700; font-size: 15px; }
.bot-id { font-family: var(--mono); font-size: 12px; color: var(--ink-muted); }
.bot-line { display: flex; justify-content: space-between; font-size: 12px; color: var(--ink-muted); gap: 10px; flex-wrap: wrap; }
.bot-line b { color: var(--ink); font-weight: 700; }
.chips { display: flex; gap: 6px; flex-wrap: wrap; }

/* ========================================================= service cards = */

/* Header for a list of cards, where a .card-head would be wrong because the
   heading sits above the cards rather than inside one. */
.list-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.svc-list { display: flex; flex-direction: column; gap: 14px; }


/* Every row reflows to the width it is given; nothing here scrolls sideways. */
/* A service card reads top to bottom: what it is and how it is doing, then
   its numbers, then what you can do about it. */
.svc-card { padding: 18px; display: flex; flex-direction: column; gap: 12px; }
.svc-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.svc-main { min-width: 0; }
.svc-name { font-weight: 700; font-size: 15px; }
.svc-status { align-items: flex-end; text-align: right; }
.svc-meta { display: flex; gap: 20px; flex-wrap: wrap; min-width: 0; }
.svc-field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.svc-field .k {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-muted);
  font-weight: 700;
}
.svc-field .v { font-size: 13px; font-weight: 600; }
.svc-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }

/* ============================================================ price cards = */

/* Five cards since «Менежмент»: narrow enough that a desktop's width takes
   them in one row rather than leaving the fifth by itself on a second. */
.price-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); gap: 16px; }
.price-card { padding: 20px; display: flex; flex-direction: column; gap: 8px; border-radius: 20px; }
.price-glyph {
  width: 44px; height: 44px; border-radius: 14px;
  display: grid; place-items: center;
  color: #fff; font-weight: 700; font-size: 16px;
  box-shadow: 0 8px 18px rgba(108, 92, 255, 0.28);
}
.price-name { font-weight: 700; font-size: 15px; margin-top: 4px; }
.price-desc { font-size: 12px; color: var(--ink-muted); flex: 1; }
.price-amount { font-family: var(--mono); font-weight: 400; font-size: 15px; }

.glyph-main { background: linear-gradient(135deg, #5aa0ff, #1877f2); }
.glyph-extra { background: linear-gradient(135deg, #48c78e, #2e9d5a); }
.glyph-ai { background: linear-gradient(135deg, #7c5cff, #5b8cff); }
.glyph-mgmt { background: linear-gradient(135deg, #1fa37a, #2f7de1); }
.glyph-dev { background: linear-gradient(135deg, #ffb347, #ff7a90); }

h2.section-title { margin: 0 0 12px; font-size: 18px; font-weight: 700; letter-spacing: -0.01em; }

/* ========================================================= product detail = */

.detail { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(min(280px, 100%), 1fr); gap: 20px; align-items: start; }
@media (max-width: 900px) { .detail { grid-template-columns: 1fr; } }
.detail-main { display: flex; flex-direction: column; gap: 20px; }
.detail-side { display: flex; flex-direction: column; gap: 20px; position: sticky; top: 18px; }

.platform-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 18px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.65);
}
.platform-row:last-child { border-bottom: 0; }
.platform-row.is-off { opacity: 0.8; }
.platform-icon {
  width: 38px; height: 38px; border-radius: 12px; flex: none;
  display: grid; place-items: center;
  color: #fff; font-weight: 700; font-size: 13px;
  box-shadow: 0 6px 14px rgba(90, 100, 150, 0.22);
}
.platform-row.is-off .platform-icon { filter: grayscale(1); opacity: 0.55; }
.platform-info { flex: 1; min-width: 0; }
.platform-name { font-weight: 700; font-size: 14px; }
.platform-name .main-tag { font-size: 11px; color: var(--ink-muted); font-weight: 600; }
.platform-note { font-size: 12px; color: var(--ink-muted); }
.platform-price { font-family: var(--mono); font-size: 13px; color: var(--ink-soft); }

.summary-line { display: flex; justify-content: space-between; gap: 12px; font-size: 13px; color: var(--ink-soft); }
.summary-line .amt { font-family: var(--mono); }
.summary-rule { height: 1px; background: rgba(120, 130, 170, 0.2); margin: 2px 0; }
.summary-total { display: flex; justify-content: space-between; gap: 12px; font-size: 15px; font-weight: 700; }
.summary-total .amt { font-family: var(--mono); font-weight: 400; }

.invoice-preview {
  padding: 12px;
  border-radius: var(--r-sm);
  background: rgba(124, 92, 255, 0.1);
  border: 1px solid rgba(124, 92, 255, 0.28);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.invoice-preview .hd { font-size: 12px; font-weight: 700; color: #3b2a90; }

.hint { font-size: 11px; color: var(--ink-faint); line-height: 1.45; }

/* =============================================================== services = */

.service-list { display: flex; flex-direction: column; gap: 14px; }
.service-card {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  text-align: left;
  width: 100%;
  padding: 18px;
  border-radius: 20px;
  cursor: pointer;
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-edge);
  box-shadow: var(--shadow-card);
}
.service-card[aria-pressed="true"] {
  border-color: rgba(124, 92, 255, 0.6);
  box-shadow: 0 0 0 3px rgba(124, 92, 255, 0.18), var(--shadow-card);
}
.service-glyph {
  width: 44px; height: 44px; border-radius: 14px; flex: none;
  display: grid; place-items: center; color: #fff; font-weight: 700;
}
.service-body { flex: 1; display: flex; flex-direction: column; gap: 4px; }
.service-name { font-weight: 700; font-size: 15px; }
.service-desc { font-size: 12.5px; color: var(--ink-muted); line-height: 1.5; }
.service-eta { font-size: 12px; color: var(--ink-faint); }
.service-price { font-family: var(--mono); font-weight: 400; font-size: 15px; white-space: nowrap; }
.service-check {
  width: 20px; height: 20px; border-radius: 50%; margin-left: auto; margin-top: 8px;
  border: 2px solid rgba(124, 92, 255, 0.35);
}
.service-card[aria-pressed="true"] .service-check { background: var(--brand); border-color: transparent; }

/* =============================================================== invoices = */

.inv-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  flex-wrap: wrap;
  border-bottom: 1px solid rgba(255, 255, 255, 0.65);
}
.inv-row:last-child { border-bottom: 0; }
.inv-row:hover { background: rgba(255, 255, 255, 0.35); }

.doc {
  padding: clamp(22px, 4vw, 48px);
  position: relative;
  overflow: hidden;
  max-width: 760px;
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: var(--glass-blur-lg);
  -webkit-backdrop-filter: var(--glass-blur-lg);
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: 26px;
  box-shadow: 0 24px 60px rgba(90, 100, 150, 0.18), inset 0 1px 0 #fff;
}
/* The banded bar across the head of the document. */
.doc::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 6px;
  background: linear-gradient(90deg, #7c5cff, #5b8cff, #48c78e);
}
.doc-org { display: flex; gap: 12px; align-items: flex-start; }
.doc-org .brand-mark { width: 42px; height: 42px; font-size: 19px; }
.doc-name { font-weight: 700; font-size: 18px; }

/* Sits over the middle of the document, clear of the header block. */
.stamp {
  position: absolute;
  right: 8%;
  top: 22%;
  padding: 8px 18px;
  border: 4px double #2e9d5a;
  border-radius: 8px;
  color: #2e9d5a;
  font-weight: 700;
  font-size: 26px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  transform: rotate(-12deg);
  opacity: 0.9;
  animation: stamp 0.35s ease-out;
  pointer-events: none;
  text-align: center;
}
.stamp small {
  display: block;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.05em;
  font-family: var(--mono);
  text-transform: none;
}

@keyframes stamp {
  0% { transform: rotate(-12deg) scale(1.6); opacity: 0; }
  100% { transform: rotate(-12deg) scale(1); opacity: 0.9; }
}

/* ================================================================ profile = */

.profile-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); gap: 20px; align-items: start; }
.profile-id { display: flex; align-items: center; gap: 12px; }
.profile-id .avatar { width: 48px; height: 48px; font-size: 16px; }
.profile-name { font-weight: 700; font-size: 16px; }
.profile-sub { font-size: 12px; color: var(--ink-muted); }
.topup-row { display: flex; gap: 8px; flex-wrap: wrap; }
.topup-row button {
  padding: 9px 15px; font-size: 13px; font-weight: 700; border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.9); cursor: pointer;
  background: var(--raised); color: #3c3f5c; box-shadow: var(--shadow-out);
}
.topup-row button[aria-pressed="true"] {
  background: var(--brand); color: #fff; border-color: transparent;
  box-shadow: 0 6px 16px rgba(108, 92, 255, 0.35);
}

/* ================================================================ tickets = */

.ticket-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(min(300px, 100%), 1.2fr); gap: 20px; align-items: start; }
@media (max-width: 900px) { .ticket-grid { grid-template-columns: 1fr; } }

.ticket-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  padding: 14px 18px;
  border: 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.65);
  background: transparent;
  cursor: pointer;
}
.ticket-row:last-child { border-bottom: 0; }
.ticket-row:hover { background: rgba(255, 255, 255, 0.4); }
.ticket-row[aria-current="true"] { background: rgba(255, 255, 255, 0.6); }
.ticket-subject { font-weight: 700; font-size: 14px; }
.ticket-meta { font-size: 12px; color: var(--ink-muted); margin-top: 2px; }
.ticket-chev { color: var(--ink-faint); font-size: 18px; }

/* overflow-x is spelled out because a lone overflow-y turns the other axis
   into a scroll container, and nothing in this panel scrolls sideways. */
.thread {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 18px;
  max-height: 460px;
  overflow-y: auto;
  overflow-x: hidden;
}
.msg { display: flex; flex-direction: column; gap: 4px; max-width: 86%; }
.msg-user { align-self: flex-end; align-items: flex-end; }
.msg-support { align-self: flex-start; }
.msg-who { font-size: 11px; color: var(--ink-muted); font-weight: 700; }
.msg-who span { font-weight: 500; }
.msg-bubble { padding: 11px 14px; border-radius: var(--r-sm); font-size: 13.5px; line-height: 1.5; }
.msg-support .msg-bubble {
  background: var(--glass-strong);
  border: 1px solid rgba(255, 255, 255, 0.95);
  box-shadow: 0 4px 12px rgba(90, 100, 150, 0.1);
}
.msg-user .msg-bubble { background: var(--brand); color: #fff; box-shadow: 0 6px 16px rgba(108, 92, 255, 0.3); }

.reply-bar { display: flex; gap: 8px; padding: 14px 18px; border-top: 1px solid var(--glass-edge); }
/* Pictures with a request: the ones chosen, small, each with a cross; and the ones sent, under their message. */
.reply-pictures { padding: 10px 18px 0; border-top: 1px solid var(--glass-edge); }
.reply-pictures + .reply-bar { border-top: 0; padding-top: 10px; }
.attach { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 6px; }
.reply-pictures .attach { margin-top: 0; }
.attach-item { position: relative; display: inline-flex; }
.attach-item img { width: 58px; height: 58px; object-fit: cover; border-radius: 10px; box-shadow: var(--shadow-out-sm); }
.attach-x {
  position: absolute; top: -6px; right: -6px; width: 20px; height: 20px; padding: 0; border: 0; border-radius: 50%;
  background: var(--ink); color: #fff; font-size: 10px; line-height: 20px; cursor: pointer;
}
.attach-pick {
  display: inline-flex; align-items: center; gap: 4px; padding: 7px 12px; border-radius: 10px; cursor: pointer;
  border: 1px dashed rgba(110, 113, 145, 0.5); color: var(--ink-soft); font-size: 13px; font-weight: 600;
}
.attach-pick:hover { color: var(--ink); border-color: var(--violet); }
.attach-wait { display: inline-flex; align-items: center; gap: 6px; color: var(--ink-muted); font-size: 12.5px; }
.msg-images { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.msg-user .msg-images { justify-content: flex-end; }
.msg-images img { display: block; max-width: 180px; max-height: 180px; border-radius: 10px; object-fit: cover; box-shadow: var(--shadow-out-sm); }
.closed-note { padding: 14px 18px; font-size: 13px; color: var(--ink-muted); border-top: 1px solid var(--glass-edge); }

/* ================================================================== modal = */

.backdrop {
  position: fixed;
  inset: 0;
  z-index: 20;
  background: rgba(35, 36, 58, 0.35);
  backdrop-filter: blur(6px);
  display: grid;
  place-items: center;
  padding: 20px;
}
.modal {
  width: min(440px, 100%);
  max-height: 90vh;
  overflow-y: auto;
  overflow-x: hidden;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: var(--glass-blur-lg);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-modal);
  animation: fade-in 0.18s ease;
}
.modal-head {
  padding: 18px 22px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  border-bottom: 1px solid var(--glass-edge);
}
.modal-title { font-weight: 700; font-size: 17px; }
.modal-sub { font-size: 12px; color: var(--ink-muted); font-family: var(--mono); }
.modal-body { padding: 18px 22px 22px; display: flex; flex-direction: column; gap: 14px; }
/* A member of staff in this customer's session (app.js asStaffBar). */
.as-staff-bar {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap;
  padding: 8px 16px; background: #2b2350; color: #fff; font-size: 13px; text-align: center;
}
.as-staff-bar .btn { background: rgba(255, 255, 255, 0.16); color: #fff; border-color: transparent; }
/* A question asked before something is done (app.js ask): over any dialog already open. */
.backdrop.is-asking { z-index: 40; }
.ask { width: min(400px, 100%); }
.ask-text { margin: 0; color: var(--ink-soft, inherit); line-height: 1.5; }
.ask-actions { display: flex; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }
.btn-primary.is-danger { background: var(--red-ink); box-shadow: none; }
.icon-btn { border: 0; background: none; cursor: pointer; font-size: 16px; color: var(--ink-soft); padding: 4px 8px; }

/* Where a transfer goes: read off a phone and typed into a bank's app, so it is
   set large, and each figure has a button that copies it. */
.bank-grid { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 10px 12px; font-size: 15px; align-items: center; }
.bank-grid span { color: var(--ink-muted); font-size: 14px; }
.bank-grid b { text-align: right; overflow-wrap: anywhere; font-size: 16px; }
.bank-grid b.mono { font-size: 16.5px; letter-spacing: 0.01em; user-select: all; }
.copy-btn {
  border: 1px solid var(--line, rgba(27, 29, 54, 0.14)); border-radius: 9px; background: rgba(255, 255, 255, 0.7);
  color: var(--ink-soft); font: inherit; font-size: 12.5px; padding: 5px 9px; cursor: pointer; white-space: nowrap;
}
.copy-btn:hover { background: #fff; color: var(--ink); }
.copy-btn.is-done { color: #1f8a5b; border-color: rgba(31, 138, 91, 0.4); }
.balance-note { font-size: 13px; color: var(--ink-soft); padding: 12px 14px; background: rgba(255, 255, 255, 0.6); border-radius: var(--r-sm); }

/* The pay dialog's own furniture: what a method means, and the live checkout
   with its countdown, QR codes and wallet links. */
.pay-note { font-size: 12.5px; color: var(--ink-soft); line-height: 1.55; overflow-wrap: anywhere; }
.pay-note.is-warn {
  color: var(--amber-ink);
  background: rgba(255, 178, 58, 0.18);
  border: 1px solid rgba(255, 178, 58, 0.45);
  border-radius: 12px;
  padding: 9px 12px;
  font-weight: 600;
}

.pay-live { display: flex; flex-direction: column; gap: 14px; }
.pay-timer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 12px;
  color: var(--ink-muted);
  font-weight: 600;
  padding: 8px 12px;
  border-radius: var(--r-xs);
  background: var(--sunken);
  box-shadow: var(--shadow-in);
}
.pay-countdown { font-size: 16px; color: var(--ink); font-weight: 400; }

.pay-provider {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 14px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, 0.65);
  border: 1px solid rgba(255, 255, 255, 0.95);
}
.pay-provider-name { font-size: 13px; font-weight: 700; color: var(--ink-soft); }
.pay-qr {
  width: 168px;
  max-width: 100%;
  height: auto;
  border-radius: 12px;
  background: #fff;
  padding: 8px;
  /* A QR scans from a hard-edged grid; smoothing it would blur the cells. */
  image-rendering: pixelated;
}
.pay-links { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }

.pay-expired {
  font-size: 13px;
  line-height: 1.55;
  color: var(--amber-ink);
  background: rgba(255, 178, 58, 0.18);
  border: 1px solid rgba(255, 178, 58, 0.45);
  border-radius: var(--r-sm);
  padding: 12px 14px;
}

/* =============================================================== utility  = */

.stack { display: flex; flex-direction: column; gap: 14px; }
.inline { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.push { margin-left: auto; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 520px) { .grid-2 { grid-template-columns: 1fr; } }
.muted { color: var(--ink-muted); }
.small { font-size: 12px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* app.js draws the sidebar from 820px up and the top bar below that, so this
   stops one short. At 820px exactly — a current iPad held upright — the
   sidebar is the only navigation on the page, and a rule that hid it there
   left the customer with none. */
@media (max-width: 819px) {
  .sidebar { display: none; }
}

/* ================================================================== print = */

/* The invoice page offers «Хэвлэх / PDF», which is window.print(). Without
   this the printer gets the whole client area — navigation, buttons, the
   tinted ground — around a document that is supposed to be the only thing on
   the page. Everything but .doc is dropped, and the document itself loses the
   glass so it reads as ink on paper rather than as a screenshot. */
@media print {
  /* A4 is named rather than left to the browser, whose "Save as PDF" would
     otherwise follow its own locale and may hand an accountant a Letter page. */
  @page { size: A4; margin: 14mm; }

  html, body {
    background: #fff;
    background-image: none;
    color: #000;
  }

  /* The shell is at least a screen high, for the sidebar's sake. On paper that
     minimum holds nothing up, and it must never be what carries a one-page
     invoice over onto a second sheet. */
  html, body, .app { min-height: 0; }

  .sidebar,
  .topbar,
  .page-head,
  .balance-chip,
  .toast,
  .notice,
  .backdrop,
  .drawer-backdrop,
  .seg,
  .stats,
  .list-head,
  .reply-bar,
  .page > .inline,
  .side-nav,
  .sidebar-foot,
  .no-print {
    display: none !important;
  }

  .app, .shell, .content, .page { display: block; padding: 0; margin: 0; gap: 0; }
  .content { padding: 0; }
  /* The screen rule is main.content, which outweighs the bare class above:
     named the same way here, or its padding and its flex column both survive
     onto the paper. */
  main.content { display: block; padding: 0; }
  .page { animation: none; }

  .doc {
    max-width: none;
    padding: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    background: #fff;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    /* An overflow-hidden document would clip a long item list off the page. */
    overflow: visible;
  }
  .doc::before { display: none; }

  .stamp {
    animation: none;
    position: static;
    transform: none;
    display: inline-block;
    margin-bottom: 14px;
    opacity: 1;
  }

  .tag {
    border: 1px solid #666 !important;
    background: none !important;
    color: #000 !important;
  }

  .brand-mark {
    background: none;
    box-shadow: none;
    border: 1px solid #000;
    color: #000;
  }

  a { color: #000; text-decoration: none; }

  /* The invoice sheet (.inv-doc). Its own rules are at the end of this file
     and take every colour from a token, so the sheet is re-inked here by
     re-pointing the tokens rather than class by class: an override written in
     this block would lose to the later rule of equal weight it was meant to
     correct, and a token set on the sheet itself cannot be outvoted that way. */
  .inv-doc {
    --ink: #000;
    --ink-soft: #333;
    --green-ink: #000;
    --amber-ink: #000;
    --red-ink: #000;
    --glass: transparent;
    --glass-strong: transparent;
    --inv-line: #999;
    --inv-tint: transparent;
    --inv-tint-edge: #999;
  }

  /* A long invoice has to break across pages, which blocks and a table do
     well and a flex column does badly. These selectors name .doc or .inv-doc
     once more than the sheet's own rules do, so they outweigh them from here. */
  .doc.inv-doc { display: block; }
  .doc.inv-doc > * + * { margin-top: 18px; }
  .inv-doc .inv-meta,
  .inv-doc .inv-party,
  .inv-doc .inv-pay,
  .inv-doc tr { break-inside: avoid; }
  /* A table foot is repeated at the bottom of every page the table runs
     over, and a total under half the lines is a wrong total. As a plain row
     group it prints once, after the last line. */
  .inv-doc tfoot { display: table-row-group; }
  /* The title's overhang has the sheet's padding to sit in on screen. On
     paper there is none, and a browser may shrink the whole page to fit it. */
  .inv-doc .inv-title { margin-right: 0; }
  /* Printed upright, the stamp needs none of the room its tilt took. */
  .inv-doc .inv-pay .stamp { margin: 0; animation: none; }
}


/* =============================================================== drawer == */

/* The burger and its drawer replace the topbar nav on a phone: six
   destinations plus sign-out do not fit one row, and wrapping them cost two
   rows of the fold. */
.burger {
  width: 40px;
  height: 40px;
  flex: none;
  display: grid;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: 12px;
  background: var(--raised);
  box-shadow: var(--shadow-out);
  cursor: pointer;
}
.burger:active { transform: translateY(1px); box-shadow: var(--shadow-out-sm); }

/* Three bars from one element: the middle is the box, the outer two are
   pseudo-elements, so the cross on open is a pure transform. */
.burger-bars,
.burger-bars::before,
.burger-bars::after {
  display: block;
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: var(--ink);
  transition: transform 0.2s ease, opacity 0.15s ease;
}
.burger-bars { position: relative; }
.burger-bars::before,
.burger-bars::after {
  content: "";
  position: absolute;
  left: 0;
}
.burger-bars::before { top: -6px; }
.burger-bars::after { top: 6px; }

.burger.is-open .burger-bars { background: transparent; }
.burger.is-open .burger-bars::before { transform: translateY(6px) rotate(45deg); }
.burger.is-open .burger-bars::after { transform: translateY(-6px) rotate(-45deg); }

/* Outstanding invoices stay visible on the bar itself — putting the only
   signal of unpaid money behind a closed menu would bury it. */
.topbar-alert {
  margin-left: auto;
  min-width: 28px;
  height: 28px;
  padding: 0 9px;
  border: 0;
  border-radius: 999px;
  background: linear-gradient(135deg, #ff7a90, #ff9a62);
  box-shadow: 0 3px 8px rgba(255, 120, 120, 0.4);
  color: #fff;
  font-weight: 700;
  font-size: 13px;
  cursor: pointer;
}

.drawer-backdrop {
  position: fixed;
  inset: 0;
  z-index: 30;
  background: rgba(35, 36, 58, 0.4);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  animation: fade-in 0.15s ease;
}

.drawer {
  position: absolute;
  inset: 0 auto 0 0;
  width: min(300px, 84vw);
  display: flex;
  flex-direction: column;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: var(--glass-blur-lg);
  -webkit-backdrop-filter: var(--glass-blur-lg);
  border-right: 1px solid var(--glass-edge);
  box-shadow: 8px 0 40px rgba(90, 100, 150, 0.25);
  animation: drawer-in 0.22s cubic-bezier(0.22, 0.61, 0.36, 1);
  overflow-y: auto;
}

@keyframes drawer-in {
  from { transform: translateX(-100%); }
  to { transform: none; }
}

.drawer-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px 14px 10px;
}
.drawer-head .brand-mark { width: 38px; height: 38px; border-radius: 13px; font-size: 17px; }

/* Full-width rows: a thumb needs a bigger target than a segmented control. */
.drawer .side-nav { padding: 6px 12px; gap: 6px; }
.drawer .side-nav button { padding: 13px 12px; font-size: 15px; }

.drawer-foot {
  margin-top: auto;
  padding: 14px 12px;
  border-top: 1px solid var(--glass-edge);
  display: flex;
  align-items: center;
  gap: 10px;
}

@media (prefers-reduced-motion: reduce) {
  .drawer, .drawer-backdrop { animation: none; }
  .burger-bars, .burger-bars::before, .burger-bars::after { transition: none; }
}

/* ================================================== order confirmation == */

.coupon-row { display: flex; gap: 8px; align-items: stretch; }
.coupon-row .input { flex: 1; min-width: 0; text-transform: uppercase; }
.coupon-row .btn { flex: none; }

.coupon-result {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 700;
}
.coupon-result.is-ok {
  color: var(--green-ink);
  background: rgba(72, 199, 142, 0.16);
  border: 1px solid rgba(72, 199, 142, 0.4);
}
.coupon-result.is-bad {
  color: var(--red-ink);
  background: rgba(255, 99, 99, 0.14);
  border: 1px solid rgba(255, 99, 99, 0.38);
}

/* A discount reads as money coming off, so it is the one green line in an
   otherwise neutral summary. */
.summary-line.discount-line { color: var(--green-ink); font-weight: 700; }

/* A register number locked while its company is being confirmed. */
.field > input[readonly] { background: var(--surface-2, #f1f2f7); color: var(--ink-muted, #6b6f85); cursor: not-allowed; }

/* ------------------------------------------------------ new-password meter */

.pw-group { display: flex; flex-direction: column; gap: 8px; }
.pw-meter { display: flex; align-items: center; gap: 10px; margin-top: -2px; }
.pw-bar { flex: 1; height: 6px; border-radius: 999px; background: rgba(120, 124, 160, 0.18); overflow: hidden; }
.pw-bar > span { display: block; height: 100%; width: 0; border-radius: inherit; transition: width 0.2s, background 0.2s; }
.pw-bar.is-weak > span { background: #e0566b; }
.pw-bar.is-fair > span { background: #e8a33d; }
.pw-bar.is-good > span { background: #4f8ef7; }
.pw-bar.is-strong > span { background: #2eaa6e; }
.pw-word { font-size: 12px; color: var(--ink-soft, #555a75); min-width: 88px; text-align: right; }
.pw-rules { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 2px 12px; font-size: 12px; color: var(--ink-faint, #8a8ea8); }
.pw-rules li::before { content: '○ '; }
.pw-rules li.is-met { color: #2eaa6e; }
.pw-rules li.is-met::before { content: '✓ '; }
.pw-match { font-size: 12px; min-height: 16px; }
.pw-match.is-met { color: #2eaa6e; }
.pw-match.is-bad { color: #e0566b; }

/* Entrance animations play once, when the element first appears. render()
   rebuilds the tree on every change and marks only what is new with
   .is-entering (app.js markEntrances), so a click never replays them. */
.page:not(.is-entering),
.page:not(.is-entering) .stamp,
.toast:not(.is-entering),
.backdrop:not(.is-entering),
.modal:not(.is-entering),
.drawer:not(.is-entering),
.drawer-backdrop:not(.is-entering) { animation: none; }

/* ============================================================ docs link == */

/* «Заавар» leaves the panel, so it is an anchor among the navigation's
   buttons. It is dressed as one of them, and it is never the current page. */
.side-nav a.nav-ext {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  font-size: 14px;
  font-weight: 700;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  color: var(--ink-soft);
  text-decoration: none;
}
.side-nav a.nav-ext:hover { color: var(--violet-deep); text-decoration: none; }
.drawer .side-nav a.nav-ext { padding: 13px 12px; font-size: 15px; }

.nav-out { font-size: 12px; color: var(--ink-faint); }

/* On the top bar the link sits against the unpaid count, which would
   otherwise share the free space with it and leave it adrift mid-bar. On a
   phone the bar has no room for it on one row, and the drawer carries it. */
.topbar a.nav-ext + .topbar-alert { margin-left: 0; }
@media (max-width: 480px) {
  .topbar a.nav-ext { display: none; }
}

/* ====================================================== invoice document = */

/* The invoice page lays a formal нэхэмжлэх out on the .doc sheet: letterhead
   and status, the dates, the two parties, the lines with their total, then how
   it is — or was — paid. Every colour here is a token, which is what lets the
   print block re-ink the whole sheet by re-pointing the tokens. Small print is
   --ink-soft, not --ink-muted: these are the lines an accountant reads, and
   muted ink on glass sits at about 4.5:1 and drops under it wherever the
   tinted ground shows through. */

.inv-page {
  /* The sheet's hairlines and its one tint, named so that print can swap them. */
  --inv-line: rgba(120, 130, 170, 0.28);
  --inv-tint: rgba(124, 92, 255, 0.07);
  --inv-tint-edge: rgba(124, 92, 255, 0.22);
}

/* As wide as the sheet beneath it, so its buttons end where the sheet ends. */
.inv-toolbar { max-width: 760px; }

.inv-doc { display: flex; flex-direction: column; gap: 22px; }

.inv-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: 16px 20px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--inv-line);
}
.inv-head .doc-org { align-items: center; min-width: 0; }
.inv-heading {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  text-align: right;
}
.inv-title {
  /* Letter-spacing trails the last letter as well; the margin takes that back,
     so the title ends flush with the lines beneath it. */
  margin: 0 -0.14em 0 0;
  font-size: 24px;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.inv-number { font-size: 15px; color: var(--ink-soft); }
.inv-number .mono { font-size: 18px; color: var(--ink); }

/* The status is its words and its mark; the colour only repeats them. The
   ground is near-white rather than a tint of the same hue, which is what
   keeps the green and the amber above 4.5:1. */
.inv-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border: 1px solid currentColor;
  border-radius: 10px;
  background: var(--glass-strong);
  color: var(--ink-soft);
  font-size: 13px;
  font-weight: 700;
  line-height: 1.3;
  white-space: nowrap;
}
.inv-status.is-paid { color: var(--green-ink); }
.inv-status.is-unpaid { color: var(--amber-ink); }
.inv-status.is-overdue { color: var(--red-ink); }

/* The dates, and for a settled invoice when and how it was paid. Two to a
   row: four across would leave «Нэхэмжилсэн огноо» no room for its own label. */
.inv-meta {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px 18px;
  padding: 14px 16px;
  border: 1px solid var(--inv-tint-edge);
  border-radius: var(--r-sm);
  background: var(--inv-tint);
}
.inv-meta dt,
.inv-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-soft);
}
.inv-meta dd { margin: 3px 0 0; font-size: 14px; font-weight: 700; overflow-wrap: anywhere; }
.inv-meta dd.mono { font-size: 15px; font-weight: 400; }
.inv-label { margin: 0 0 6px; }

/* Нэхэмжлэгч beside Төлөгч; on a phone, one above the other (see below). */
.inv-parties {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
}
.inv-party {
  min-width: 0;
  padding: 14px 16px;
  border: 1px solid var(--inv-line);
  border-radius: var(--r-sm);
  background: var(--glass);
}
.inv-party-name { font-size: 15px; font-weight: 700; overflow-wrap: anywhere; }

/* «ТТД: 80201707445» — a label and its value on one line, wrapping as a
   sentence would rather than as a two-column grid that starves the value. */
.inv-facts { margin: 6px 0 0; font-size: 13px; line-height: 1.6; }
.inv-facts dt,
.inv-facts dd { display: inline; margin: 0; }
.inv-facts dt { color: var(--ink-soft); }
.inv-facts dd { overflow-wrap: anywhere; }

table.inv-items { width: 100%; border-collapse: collapse; font-size: 13px; }
table.inv-items th,
table.inv-items td { padding: 11px 0; text-align: left; vertical-align: top; }
table.inv-items thead th {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-soft);
  border-bottom: 2px solid var(--inv-line);
}
table.inv-items tbody td { border-bottom: 1px solid var(--inv-line); }
/* № and Дүн take the room their content needs and no more. The description
   has the rest of the row and is the only cell that wraps, which is how the
   table fits a phone without scrolling sideways. */
table.inv-items .inv-col-no {
  width: 1%;
  padding-right: 14px;
  white-space: nowrap;
  color: var(--ink-soft);
}
table.inv-items .inv-col-amt {
  width: 1%;
  padding-left: 14px;
  white-space: nowrap;
  text-align: right;
}
table.inv-items .inv-col-desc { overflow-wrap: anywhere; }
/* Money coming off — a discount, a part paid from the balance. The minus is
   in the figure itself; the green only echoes it. */
table.inv-items tr.is-credit .inv-col-desc,
table.inv-items tr.is-credit .inv-col-amt { color: var(--green-ink); }
table.inv-items tfoot th,
table.inv-items tfoot td {
  padding: 14px 0 0;
  border-top: 2px solid var(--ink);
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
  vertical-align: baseline;
}
table.inv-items tfoot th { text-align: right; }
table.inv-items tfoot td { font-size: 18px; }

.inv-pay {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px 24px;
  padding-top: 18px;
  border-top: 1px solid var(--inv-line);
}
.inv-pay-info { flex: 1 1 240px; min-width: 0; }
.inv-pay-info .inv-facts { margin-top: 0; }
/* The button, like the stamp below, keeps to the right when the block is too
   narrow to hold it beside the details and it drops to a row of its own. */
.inv-pay .btn-primary { flex: none; margin-left: auto; }

/* The paid stamp takes the pay button's place, in the flow of the sheet.
   Floated over the sheet, as .stamp's own rule would have it, it would lie
   across the payer's details. It takes --green-ink for the sake of the small
   print inside it: the stamp's own lighter green, at its own opacity, gives
   that line about 3:1. */
.inv-pay .stamp {
  position: static;
  flex: none;
  /* Room for the corners its tilt swings out. */
  margin: 16px 14px 16px auto;
  border-color: var(--green-ink);
  color: var(--green-ink);
  opacity: 1;
  animation-name: stamp-in;
}
/* The entrance that .stamp itself names ends at that rule's .9 opacity. This
   one ends at 1, where this stamp rests, so it does not jump as it lands.
   Like every entrance it plays once: the .is-entering rule above outweighs
   the name set here. */
@keyframes stamp-in {
  0% { transform: rotate(-12deg) scale(1.6); opacity: 0; }
  100% { transform: rotate(-12deg) scale(1); opacity: 1; }
}

/* A phone has no room for anything side by side. The title drops beneath the
   brand and reads from the left like everything under it, the two parties
   stack, and each date becomes one line: its label at the left, its value at
   the right. Print never comes through here — a page is wider than this. */
@media (max-width: 480px) {
  .inv-head,
  .inv-meta,
  .inv-parties { grid-template-columns: minmax(0, 1fr); }
  .inv-heading { align-items: flex-start; text-align: left; }
  .inv-title { font-size: 20px; }
  .inv-meta > div {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 12px;
  }
  /* The margin, not the row's justification, holds the value to the right:
     it still does when the value has to wrap beneath a long label. */
  .inv-meta dd { margin: 0 0 0 auto; text-align: right; }
  .inv-pay .btn-primary { width: 100%; }
  .inv-pay .stamp { margin-left: auto; margin-right: auto; font-size: 20px; }
}

@media (prefers-reduced-motion: reduce) {
  .inv-pay .stamp { animation: none; }
}

/* ===================================================== dashboard, redrawn == */

/* The small pictures app.js draws beside a menu entry, a figure or an action.
   They take the colour of whatever they sit in. */
.ico { display: inline-grid; place-items: center; flex: none; }
.ico svg { display: block; width: 1.15em; height: 1.15em; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

/* A menu entry's picture, on the colour its dot has always had. */
.nav-ico {
  display: grid;
  place-items: center;
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 10px;
  color: #fff;
  box-shadow: 0 4px 10px rgba(90, 100, 150, 0.24), inset 0 1px 0 rgba(255, 255, 255, 0.35);
  transition: transform 0.25s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.nav-ico .ico svg { width: 16px; height: 16px; }
.side-nav button:hover .nav-ico,
.side-nav a.nav-ext:hover .nav-ico { transform: translateY(-1px) rotate(-5deg) scale(1.07); }
/* Deeper than the dots were: a white outline has to read on them. */
.nav-ico.nav-dot-products { background: linear-gradient(135deg, #2fb37a, #1d8754); }
.nav-ico.nav-dot-services { background: linear-gradient(135deg, #f59a2f, #e8577a); }
.nav-ico.nav-dot-invoices { background: linear-gradient(135deg, #3f9df5, #2f6fe0); }
.nav-ico.nav-dot-tickets { background: linear-gradient(135deg, #f0607c, #f08a4a); }
.nav-ico.nav-dot-docs { background: linear-gradient(135deg, #7b7f9c, #4d5070); }

/* The four figures: each lifts under the pointer and carries its picture. */
.stat { transition: transform 0.22s cubic-bezier(0.2, 0.7, 0.2, 1), box-shadow 0.22s; }
.stat:hover { transform: translateY(-3px); box-shadow: var(--shadow-panel); }
.stat .label { padding-right: 40px; }
.stat-ico {
  position: absolute;
  right: 14px;
  top: 14px;
  width: 32px;
  height: 32px;
  border-radius: 11px;
  background: rgba(255, 255, 255, 0.78);
  box-shadow: var(--shadow-out-sm);
}
.stat-ico svg { width: 17px; height: 17px; }
.ink-green { color: var(--green-ink); }
.ink-amber { color: var(--amber-ink); }
.ink-violet { color: var(--violet-deep); }
.ink-blue { color: #2f62d8; }

.dash-col { display: flex; flex-direction: column; gap: 20px; min-width: 0; }

/* What a customer most often comes to do, one press away. */
.quicks { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr)); gap: 14px; }
.quick {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 14px;
  border-radius: 18px;
  border: 1px solid var(--glass-edge);
  background: var(--glass-strong);
  box-shadow: var(--shadow-card);
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: transform 0.22s cubic-bezier(0.2, 0.7, 0.2, 1), box-shadow 0.22s;
}
.quick:hover { transform: translateY(-3px); box-shadow: var(--shadow-panel); }
.quick:active { transform: scale(0.985); }
.quick-ico {
  width: 38px;
  height: 38px;
  border-radius: 13px;
  color: #fff;
  box-shadow: 0 6px 14px rgba(90, 100, 150, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.35);
  transition: transform 0.3s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.quick-ico svg { width: 19px; height: 19px; }
.quick:hover .quick-ico { transform: rotate(-6deg) scale(1.08); }
.quick-violet .quick-ico { background: var(--brand); }
.quick-amber .quick-ico { background: linear-gradient(135deg, #f59a2f, #e8577a); }
.quick-green .quick-ico { background: linear-gradient(135deg, #2fb37a, #1d8754); }
.quick-blue .quick-ico { background: linear-gradient(135deg, #3f9df5, #2f6fe0); }
.quick-pink .quick-ico { background: linear-gradient(135deg, #f0607c, #b65be0); }
.quick-text { flex: 1; min-width: 0; display: grid; line-height: 1.3; }
.quick-text b { font-size: 14px; font-weight: 700; }
.quick-text span { font-size: 12px; color: var(--ink-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.quick-go { color: var(--ink-faint); transition: transform 0.22s cubic-bezier(0.2, 0.7, 0.2, 1), color 0.2s; }
.quick-go svg { width: 16px; height: 16px; }
.quick:hover .quick-go { transform: translateX(3px); color: var(--violet-deep); }

/* What was paid, month by month. One colour: every bar is the same thing, and
   its height against the others is what there is to read. The figure over
   each bar and the month under it are text, in the text's own colours. */

/* How much of a chatbot's paid time is left. The words beside the bar say it
   too; the colour only says how soon. The track is a pale step of the fill. */
.term { display: flex; flex-direction: column; gap: 6px; }
.term-bar { height: 8px; border-radius: 999px; overflow: hidden; background: rgba(72, 199, 142, 0.2); }
.term-bar > span { display: block; height: 100%; min-width: 4px; border-radius: inherit; background: linear-gradient(90deg, #48c78e, #2e9d5a); transform-origin: left; }
.term-amber .term-bar { background: rgba(255, 179, 71, 0.24); }
.term-amber .term-bar > span { background: linear-gradient(90deg, #ffb347, #f08a24); }
.term-amber b { color: var(--amber-ink); }
.term-red .term-bar { background: rgba(255, 122, 144, 0.24); }
.term-red .term-bar > span { background: linear-gradient(90deg, #ff7a90, #e0566b); }
.term-red b { color: var(--red-ink); }

.bot-card { transition: transform 0.22s cubic-bezier(0.2, 0.7, 0.2, 1), box-shadow 0.22s; }
.bot-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-panel); }

/* Arrival. These play once, with the page they belong to: render() rebuilds
   the tree on every change and marks only a page that has just appeared. */
@keyframes rise-in { from { opacity: 0; transform: translateY(12px); } }
@keyframes grow-up { from { transform: scaleY(0); } }
@keyframes grow-right { from { transform: scaleX(0); } }
/* Every page arrives top to bottom, block by block. */
.page.is-entering > * { animation: rise-in 0.45s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; }
.page.is-entering > :nth-child(2) { animation-delay: 0.06s; }
.page.is-entering > :nth-child(3) { animation-delay: 0.12s; }
.page.is-entering > :nth-child(n + 4) { animation-delay: 0.18s; }
.page.is-entering .stat { animation: rise-in 0.45s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; animation-delay: calc(var(--i, 0) * 60ms); }
.page.is-entering .quick { animation: rise-in 0.45s cubic-bezier(0.2, 0.7, 0.2, 1) 0.18s backwards; }
.page.is-entering .quick:nth-child(2) { animation-delay: 0.23s; }
.page.is-entering .quick:nth-child(3) { animation-delay: 0.28s; }
.page.is-entering .quick:nth-child(4) { animation-delay: 0.33s; }
.page.is-entering .term-bar > span { animation: grow-right 0.8s cubic-bezier(0.2, 0.7, 0.2, 1) 0.3s backwards; }

@media (max-width: 480px) {
  /* Two figures to a row on a phone: four in a column pushed everything else
     a screen down. */
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .stat { padding: 14px 14px 13px; }
  .stat .value { font-size: 20px; }
  .stat-ico { right: 10px; top: 10px; width: 28px; height: 28px; border-radius: 10px; }
  .stat .label { padding-right: 30px; }
}

@media (prefers-reduced-motion: reduce) {
  .page.is-entering > *,
  .page.is-entering .stat,
  .page.is-entering .quick,
  .page.is-entering .term-bar > span { animation: none; }
  .stat, .stat.is-link, .quick, .quick-ico, .quick-go, .bot-card, .nav-ico { transition: none; }
  .stat:hover, .quick:hover, .bot-card:hover { transform: none; }
}

@media print {
  .quicks { display: none; }
  .page.is-entering > *, .page.is-entering .stat { animation: none; }
}

/* The quick action that brings a customer here stops with the form in view,
   clear of the bar that stays at the top on a phone. */
#topup { scroll-margin-top: 84px; }

/* ------------------------------------------------- a reseller's customers */

/* «Миний харилцагчид»: who a reseller brought. A row is the customer and
   their chatbots, then what they owe and what they have earned the reseller. */
.nav-dot-partner, .nav-ico.nav-dot-partner { background: linear-gradient(135deg, #22b8cf, #1c7ed6); }
.partner-row { align-items: flex-start; }
.partner-bots { display: grid; gap: 4px; margin-top: 8px; }
.partner-bot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: 13px; }
.partner-bot .row-sub { margin-top: 0; }
.partner-money { min-width: 132px; text-align: right; }
.partner-money .k { font-size: 11.5px; color: var(--ink-muted); }
.partner-money .v { margin-top: 3px; font-size: 13.5px; }
@media (max-width: 640px) {
  .partner-money { min-width: 0; flex: 1 1 40%; text-align: left; }
  /* The customer and their chatbots have the row to themselves; what they owe
     and what they have earned share the one beneath it. */
  .partner-row .row-main { flex-basis: 100%; }
}

/* ========================================= waiting, pressing and arriving == */

/* The rest of the customer's pages, brought to where the dashboard is: what
   is pressed answers under the pointer, what has to ask the server says so
   until the answer is back, and what appears arrives rather than snapping in.
   The same rules as there — motion on arrival and under the pointer only,
   `backwards` and never `both`, and all of it off for a customer who has
   asked their device for less (the block at the end). */

/* ------------------------------------------------------------- the spinner */

/* A request is out. It never stands alone: the words beside it say what is
   being waited for. One turn takes what SPIN_MS says in app.js, which is how
   a ring redrawn mid-turn goes on from where it was. */
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes appear { from { opacity: 0; } }

.spin {
  display: inline-block;
  flex: none;
  width: 1em;
  height: 1em;
  vertical-align: -0.16em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

/* A line that says the page itself is waiting. */
.waiting { display: flex; align-items: center; gap: 8px; margin: 0; }

/* Before app.js has drawn anything: it is still on its way, or asking who is
   signed in. Nothing shows for the first moment, so a page that opens at once
   never flashes it. */
#root:empty {
  min-height: 100vh;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 14px;
}
#root:empty::before {
  content: "";
  width: 30px;
  height: 30px;
  border: 3px solid var(--violet-deep);
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 0.8s linear infinite, appear 0.3s ease 0.4s backwards;
}
#root:empty::after {
  content: "Ачаалж байна…";
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-soft);
  animation: appear 0.3s ease 0.4s backwards;
}

/* The tab opened for the chatbot's panel, while the server is asked where it
   leads (tabWaits in app.js). */
.tab-waits {
  min-height: 100vh;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 18px;
  padding: 24px;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-soft);
  text-align: center;
}
.tab-waits .brand-mark { width: 52px; height: 52px; border-radius: 17px; font-size: 23px; }
.tab-waits .spin { color: var(--violet-deep); }

/* Where priced lines will stand, while the server is asked for them. */
@keyframes shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.skel-lines { display: flex; flex-direction: column; gap: 10px; }
.skel {
  display: block;
  height: 12px;
  border-radius: 6px;
  background: linear-gradient(90deg, rgba(120, 130, 170, 0.14) 25%, rgba(120, 130, 170, 0.28) 50%, rgba(120, 130, 170, 0.14) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
}
.skel:nth-child(2) { width: 72%; }
.skel-total { width: 56%; height: 16px; margin-top: 6px; }

/* ------------------------------------------------------ under the pointer */

/* Every button answers the pointer and the press, the way the dashboard's
   tiles do. */
.btn, .btn-primary, .btn-tonal, .btn-ghost, .seg > button, .choice, .topup-row button,
.icon-btn, .side-nav button, .side-nav a.nav-ext {
  transition: transform 0.15s cubic-bezier(0.2, 0.7, 0.2, 1), box-shadow 0.2s ease,
    filter 0.2s ease, color 0.15s ease, background-color 0.2s ease, border-color 0.2s ease;
}
.seg > button:active, .choice:active, .topup-row button:active { transform: translateY(1px); }
.side-nav button:active, .side-nav a.nav-ext:active { transform: scale(0.985); }
.service-card:active { transform: scale(0.99); }
.btn-ghost:enabled:hover { color: var(--violet-deeper); }
.btn-ghost:disabled { opacity: 0.55; cursor: not-allowed; }
/* The quiet button for what cannot be undone — «Цуцлах» beside «Төлөх». It
   is the lesser of the two at a glance: no ground and no shadow, in the ink
   a refusal is printed in. Under the pointer it takes that refusal's tint. */
.btn-ghost.is-danger { color: var(--red-ink); border-radius: 10px; }
.btn-ghost.is-danger:enabled:hover { color: var(--red-ink); background: rgba(255, 99, 99, 0.14); }
.row-actions .btn-ghost, .inv-toolbar .btn-ghost { align-self: center; }
.btn-tonal:disabled { opacity: 0.45; cursor: not-allowed; }
.icon-btn { border-radius: 10px; }

/* A card that holds one thing lifts under the pointer, and a row in a list
   takes a tint. Only where there is a pointer to be under — on a phone a
   "hover" stays put after the tap that caused it — and the lifting only for
   a customer who has not asked for less motion. */
.svc-card, .price-card, .service-card {
  transition: transform 0.22s cubic-bezier(0.2, 0.7, 0.2, 1), box-shadow 0.22s ease, border-color 0.2s ease;
}
.inv-row, .ticket-row, .row-item, .platform-row { transition: background-color 0.15s ease; }
@media (hover: hover) {
  .row-item:hover, .platform-row:hover { background: rgba(255, 255, 255, 0.35); }
  .icon-btn:hover { color: var(--violet-deep); background: rgba(124, 92, 255, 0.1); }
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .btn:enabled:hover, .btn-tonal:enabled:hover, a.btn:hover, a.btn-primary:hover { transform: translateY(-1px); }
  .btn-primary:enabled:hover {
    transform: translateY(-1px);
    box-shadow: 0 14px 28px rgba(108, 92, 255, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.35);
  }
  /* The press outweighs the lift. */
  .btn:enabled:active, .btn-tonal:enabled:active, .btn-primary:enabled:active,
  a.btn:active, a.btn-primary:active { transform: translateY(1px); }
  .svc-card:hover, .price-card:hover, .service-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-panel); }
  .service-card[aria-pressed="true"]:hover { box-shadow: 0 0 0 3px rgba(124, 92, 255, 0.18), var(--shadow-panel); }
}

/* A button whose own request is out (busyButton in app.js). It is waiting,
   not unavailable: it keeps its colour, where a disabled one fades, and its
   place in the tab order. It neither lifts nor presses meanwhile — which is
   why these stand after the rules above, and weigh as much. */
.is-busy { cursor: progress; }
.is-busy > .spin { margin-right: 0.55em; }
/* One in a row with no room to spare — «Төлөх» beside «Харах» — keeps its
   width to the pixel: the ring stands where the words were, and the words,
   unseen, still hold their room and are still what a screen reader reads. */
.is-busy.is-fixed { position: relative; }
.is-busy.is-fixed > span:last-child { opacity: 0; }
.is-busy.is-fixed > .spin { position: absolute; left: 50%; top: 50%; margin: -0.5em 0 0 -0.5em; }
.btn-primary.is-busy:enabled,
.btn-primary.is-busy:enabled:hover,
.btn-primary.is-busy:enabled:active {
  filter: none;
  transform: none;
  box-shadow: 0 4px 12px rgba(108, 92, 255, 0.26), inset 0 1px 0 rgba(255, 255, 255, 0.35);
}
.btn.is-busy:enabled,
.btn.is-busy:enabled:hover,
.btn.is-busy:enabled:active { color: var(--violet-deep); transform: none; box-shadow: var(--shadow-out-sm); }
.quick[aria-busy="true"] { cursor: progress; }
.quick > .spin { font-size: 16px; color: var(--violet-deep); }

/* A field says which one is being typed into: the page's focus ring is
   switched off for fields (they have no outline), and nothing stood in for
   it. The border is the full violet, which is what reads against the sunken
   ground; the halo only softens it. */
.field > input, .field > select, .field > textarea, .input {
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.field > input:focus-visible, .field > select:focus-visible, .field > textarea:focus-visible, .input:focus-visible {
  border-color: var(--violet-deep);
  box-shadow: var(--shadow-in), 0 0 0 3px rgba(124, 92, 255, 0.2);
}

/* A dialog and the menu are given the focus as they open, so that the
   keyboard is inside them; they are not themselves something to press. */
.modal:focus, .drawer:focus { outline: none; }

/* ------------------------------------------------------------ the sign-in */

/* The card arrives once, and each of its forms as it takes the last one's
   place: the heading first, then what is under it. */
.auth-card.is-entering { animation: rise-in 0.5s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; }
.auth-form.is-entering > * { animation: rise-in 0.4s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; }
.auth-form.is-entering > :nth-child(2) { animation-delay: 0.04s; }
.auth-form.is-entering > :nth-child(3) { animation-delay: 0.08s; }
.auth-form.is-entering > :nth-child(4) { animation-delay: 0.12s; }
.auth-form.is-entering > :nth-child(5) { animation-delay: 0.16s; }
.auth-form.is-entering > :nth-child(n + 6) { animation-delay: 0.2s; }
/* What a form says back — a refusal, a code sent again — fades in where it
   stands, so that it is seen to be new. */
.form-error.is-entering, .form-ok.is-entering { animation: fade-in 0.25s ease backwards; }

/* The code step of registration. It stands where the form stood, and has to
   read as a different thing at a glance: a picture of where the code went,
   and that sentence at reading size rather than as small print. */
.step-ico {
  align-self: flex-start;
  width: 46px;
  height: 46px;
  border-radius: 15px;
  color: #fff;
  background: var(--brand);
  box-shadow: 0 8px 18px rgba(108, 92, 255, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.35);
}
.step-ico svg { width: 22px; height: 22px; }
.step-note { margin: 0; font-size: 14px; line-height: 1.55; color: var(--ink-soft); }
.step-note b { font-size: 15px; color: var(--ink); white-space: nowrap; }

/* On a phone the card is one column, and the picture, the line under it and
   its three points stood a whole screen above the form: somebody who came to
   sign in met no field without scrolling, and the code step arrived at the
   foot of the screen. There they are a header — the name, the robot small
   beside it, the one line — and the form begins under it. The card keeps to
   the top as well, so it does not jump when a long form gives way to a short
   step. (The card goes to two columns from 650px: two of 300px, its border
   and the page's margins.) */
@media (max-width: 649px) {
  .auth-wrap { padding: 14px; place-items: start center; }
  .auth-aside {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px 14px;
    padding: 18px 22px 16px;
    border-right: 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.7);
  }
  .auth-aside > .inline { flex: 1; min-width: 0; flex-wrap: nowrap; }
  .auth-aside .robot { margin: 0; }
  .auth-aside .robot svg { display: block; width: 58px; height: 58px; }
  .auth-lede { flex-basis: 100%; font-size: 15px; line-height: 1.35; }
  .auth-points { display: none; }
  .auth-form { padding: 24px 22px 28px; justify-content: flex-start; }
}

/* ---------------------------------------------------------------- arriving */

/* Rows and cards come in one after another, inside the block they belong to,
   which is itself arriving (.page.is-entering > *). The place in the line is
   read off the row's own position; from the eighth on they come together, so
   a long list is not still arriving when it is wanted. */
.page.is-entering :is(.inv-row, .row-item, .ticket-row, .platform-row, .svc-card, .price-card, .service-card),
.inv-list.is-entering .inv-row {
  animation: rise-in 0.4s cubic-bezier(0.2, 0.7, 0.2, 1) backwards;
  animation-delay: calc(var(--n, 0) * 40ms + 0.1s);
}
/* A list re-drawn under another filter has no block arriving ahead of it. */
.inv-list.is-entering .inv-row { animation-delay: calc(var(--n, 0) * 30ms); }
:is(.inv-row, .row-item, .ticket-row, .platform-row, .svc-card, .price-card, .service-card) { --n: 0; }
:is(.inv-row, .row-item, .ticket-row, .platform-row, .svc-card, .price-card, .service-card):nth-child(2) { --n: 1; }
:is(.inv-row, .row-item, .ticket-row, .platform-row, .svc-card, .price-card, .service-card):nth-child(3) { --n: 2; }
:is(.inv-row, .row-item, .ticket-row, .platform-row, .svc-card, .price-card, .service-card):nth-child(4) { --n: 3; }
:is(.inv-row, .row-item, .ticket-row, .platform-row, .svc-card, .price-card, .service-card):nth-child(5) { --n: 4; }
:is(.inv-row, .row-item, .ticket-row, .platform-row, .svc-card, .price-card, .service-card):nth-child(6) { --n: 5; }
:is(.inv-row, .row-item, .ticket-row, .platform-row, .svc-card, .price-card, .service-card):nth-child(7) { --n: 6; }
:is(.inv-row, .row-item, .ticket-row, .platform-row, .svc-card, .price-card, .service-card):nth-child(n + 8) { --n: 7; }

/* The request a customer opens, or the form for a new one, takes the place
   of the other beside the list. */
.ticket-pane.is-entering { animation: rise-in 0.35s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; }

/* A dialog: the page dims behind it, and what it shows changes in place when
   the customer moves between the ways to pay or the steps of signing in. The
   tabs themselves stay where they are. */
.backdrop.is-entering { animation: appear 0.2s ease backwards; }
.modal-body.is-entering > :not(.seg) { animation: fade-in 0.22s ease backwards; }

/* ------------------------------------------------------------------ layout */

/* A list with nothing in it yet (emptyState in app.js). */
.empty-ico {
  width: 44px;
  height: 44px;
  border-radius: 14px;
  color: var(--violet-deep);
  background: rgba(124, 92, 255, 0.1);
}
.empty-ico svg { width: 22px; height: 22px; }

/* How many each filter holds, in the figures' own face. */
.seg-count { font-family: var(--mono); font-weight: 400; font-size: 12px; }

/* «Нэхэмжлэхийг цуцлах» at the foot of the pay dialog: under its ways to pay,
   and apart from them. */
.modal-foot {
  display: flex;
  justify-content: center;
  padding-top: 10px;
  border-top: 1px solid rgba(120, 130, 170, 0.2);
}

/* The ordered services stand two abreast where there is room for two, each as
   tall as its neighbour with its buttons at the foot. One chatbot — most
   customers have one — still has the row to itself. */
.svc-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(440px, 100%), 1fr)); gap: 16px; }
.svc-card .svc-actions { margin-top: auto; }

/* The cards of the profile page stand as far apart as every other page's. */
.profile-grid .stack { gap: 20px; }

@media (max-width: 819px) {
  /* Under the bar that stays at the top on a phone, not behind it. */
  .toast { top: 72px; }
}
@media (max-width: 480px) {
  .page-head h1 { font-size: 24px; }
}

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

@media (prefers-reduced-motion: reduce) {
  .spin, .skel { animation: none; }
  /* What these pages already had and had never switched off: the page's own
     fade, the toast's and the dialog's, the stamp, the robot that floats, and
     the switch and the password meter that slide. */
  .page, .toast, .modal, .stamp, .robot { animation: none; }
  .switch, .switch::after, .pw-bar > span { transition: none; }
  /* Nothing turns and nothing fades; the first moment of nothing is kept,
     which is a wait and not a movement, so that a page that opens at once
     still does not flash it. */
  #root:empty::before, #root:empty::after { animation: appear 0s linear 0.4s backwards; }
  .auth-card.is-entering, .auth-form.is-entering > *,
  .form-error.is-entering, .form-ok.is-entering,
  .page.is-entering :is(.inv-row, .row-item, .ticket-row, .platform-row, .svc-card, .price-card, .service-card),
  .inv-list.is-entering .inv-row, .ticket-pane.is-entering,
  .backdrop.is-entering, .modal-body.is-entering > :not(.seg) { animation: none; }
  .btn, .btn-primary, .btn-tonal, .btn-ghost, .seg > button, .choice, .topup-row button,
  .icon-btn, .side-nav button, .side-nav a.nav-ext,
  .svc-card, .price-card, .service-card, .inv-row, .ticket-row, .row-item, .platform-row,
  .field > input, .field > select, .field > textarea, .input { transition: none; }
  .seg > button:active, .choice:active, .topup-row button:active,
  .side-nav button:active, .side-nav a.nav-ext:active, .service-card:active { transform: none; }
}

@media print {
  .page.is-entering :is(.inv-row, .row-item, .ticket-row, .platform-row, .svc-card, .price-card, .service-card) { animation: none; }
}
