/* =====================================================================
   nv-landing — the front page
   =====================================================================

   REQUIRES app/css/nv-base.css, loaded before this file. Same palette,
   same radii, same easing as the card and the menu: a visitor who arrives
   here, signs up and opens their card should not feel handed between three
   different products.

   WHAT THIS REPLACED
   ------------------
   An app shell with bottom tabs and three panels, two of which were
   `hidden` on load. That is a strange shape for a marketing page — it reads
   as an application a visitor has not agreed to use yet — and it put most
   of the page's words behind a class a crawler has no reason to unset.
   This is one document, top to bottom, every word in the HTML.

   STRUCTURE
   ---------
   .nvl-head        sticky header, anchors + one CTA
   .nvl-hero        the claim-your-address field, the whole point of the page
   .nvl-logos       who it is for
   .nvl-products    the three things it does
   .nvl-steps       how it works
   .nvl-pricing     plans, server-rendered
   .nvl-faq         questions, matching the FAQPage schema
   .nvl-contact     support, via the real ticket system
   .nvl-foot
   ===================================================================== */

/* The landing page is the only page that loads this file, so owning <body>
   here is safe — and necessary, or the strip either side of a wide layout
   paints the browser's white rather than the theme's ground. */
/* Vazirmatn, named explicitly rather than left to Tailwind's `font-vazir`
   utility on <body>. That utility only exists if the Tailwind build happens
   to define it, and on the landing page it was not applying — the page came
   up in the browser's default UI font, which for Persian text is whatever
   the OS falls back to. The @font-face lives in
   app/webfonts/Vazirmatn-font-face.css, loaded before this file.

   That file ships THREE weights — 600, 700 and 800. Nothing lighter exists,
   so the base weight here is 600 rather than a 400 the browser would have
   to synthesise or round up to anyway. */
body {
  margin: 0;
  background: var(--nv-back-bg);
  font-family: Vazirmatn, system-ui, -apple-system, 'Segoe UI', Tahoma, sans-serif;
  font-weight: 600;
}

/* Visible to a screen reader, to nothing else. Tailwind's `sr-only` would
   do, but only if the build happens to have emitted it; this cannot be
   quietly tree-shaken out from under the page. */
.nvl-sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Tailwind's preflight sets this globally in production, which is exactly
   why it is repeated here: a page-scoped stylesheet that only lays out
   correctly while a *different* stylesheet happens to be loaded is one
   build change away from a dock button 18px wider than the phone. Measured,
   not assumed — .nvl-dock .nvl-btn{width:100%} overflowed by 34px without
   it. */
.nvl, .nvl *, .nvl *::before, .nvl *::after { box-sizing: border-box; }

.nvl {
  --nvl-max: 68rem;
  --nvl-pad: clamp(1rem, 4vw, 2rem);
  --nvl-gap: clamp(3rem, 9vw, 6rem);
  --nvl-r: 20px;

  /* One accent gradient, used for the CTA and for the hero glow. Two would
     be a palette; three would be a ransom note. */
  --nvl-grad: linear-gradient(135deg, #4f46e5 0%, #7c3aed 55%, #db2777 100%);
  --nvl-ink: var(--nv-back-fg);
  --nvl-dim: var(--nv-back-muted);
  --nvl-line: var(--nv-line);
  --nvl-card: var(--nv-surface);
  --nvl-card-hi: var(--nv-surface-hi);

  color: var(--nvl-ink);
  background: var(--nv-back-bg);
  font-family: Vazirmatn, system-ui, -apple-system, 'Segoe UI', Tahoma, sans-serif;
  line-height: 1.9;
}

/* Form controls do not inherit the page font on their own — a browser gives
   them its own UI stack — and the OTP modal's inputs live outside .nvl. */
input, textarea, select, button { font-family: inherit; }

.nvl__wrap {
  width: 100%;
  max-width: var(--nvl-max);
  margin-inline: auto;
  padding-inline: var(--nvl-pad);
  box-sizing: border-box;
}

.nvl section { padding-block: var(--nvl-gap); }
.nvl section + section { border-top: 1px solid var(--nvl-line); }

/* ── type ───────────────────────────────────────────────────────────── */

.nvl h1, .nvl h2, .nvl h3 { margin: 0; line-height: 1.45; font-weight: 800; }
.nvl h1 { font-size: clamp(1.9rem, 6.5vw, 3.4rem); letter-spacing: -.02em; }
.nvl h2 { font-size: clamp(1.4rem, 4.2vw, 2.2rem); letter-spacing: -.015em; }
.nvl h3 { font-size: 1.05rem; }
.nvl p  { margin: 0; }

.nvl__lead {
  font-size: clamp(.95rem, 2.6vw, 1.15rem);
  color: var(--nvl-dim);
  max-width: 46ch;
}
.nvl__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: .3rem .75rem;
  border-radius: 999px;
  border: 1px solid var(--nvl-line);
  background: var(--nvl-card);
  color: var(--nvl-dim);
  font-size: .74rem;
  font-weight: 700;
}
.nvl__head-block { display: grid; gap: .7rem; margin-bottom: 2.4rem; }

/* ── header ─────────────────────────────────────────────────────────── */

.nvl-head {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--nv-back-bg) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(12px);
  -webkit-backdrop-filter: saturate(1.4) blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color .25s var(--nv-ease-soft);
}
.nvl-head.is-stuck { border-bottom-color: var(--nvl-line); }
.nvl-head__in {
  display: flex;
  align-items: center;
  gap: 1rem;
  height: 62px;
}
.nvl-brand {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  font-weight: 900;
  font-size: 1.1rem;
  color: var(--nvl-ink);
  text-decoration: none;
  flex: none;
}
.nvl-brand img { width: 30px; height: 30px; border-radius: 9px; }
.nvl-nav { display: none; gap: .3rem; flex: 1; }
@media (min-width: 900px) { .nvl-nav { display: flex; } }
.nvl-nav a {
  padding: .4rem .7rem;
  border-radius: 10px;
  color: var(--nvl-dim);
  font-size: .86rem;
  font-weight: 600;
  text-decoration: none;
  transition: background-color .18s var(--nv-ease-soft), color .18s var(--nv-ease-soft);
}
.nvl-nav a:hover { background: var(--nvl-card); color: var(--nvl-ink); }
.nvl-head__end { margin-inline-start: auto; display: flex; gap: .5rem; align-items: center; }

/* Below ~380px the wordmark and two buttons do not fit on one line, and the
   one that goes is "ورود": the same door is behind "ساخت رایگان" for
   someone who already has an account, so nothing becomes unreachable. */
@media (max-width: 380px) {
  .nvl-head__end .nvl-btn--ghost { display: none; }
}

/* ── buttons ────────────────────────────────────────────────────────── */

.nvl-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .45rem;
  min-height: 44px;
  padding: .6rem 1.15rem;
  border: 1px solid transparent;
  border-radius: 13px;
  font: inherit;
  font-size: .92rem;
  font-weight: 800;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: transform .14s var(--nv-ease-soft), filter .2s var(--nv-ease-soft),
              background-color .2s var(--nv-ease-soft);
}
.nvl-btn:active { transform: scale(.97); }
.nvl-btn--primary { background: var(--nvl-grad); color: #fff; box-shadow: 0 12px 26px -14px #4f46e5; }
.nvl-btn--primary:hover { filter: brightness(1.07); }
.nvl-btn--ghost { background: var(--nvl-card); color: var(--nvl-ink); border-color: var(--nvl-line); }
.nvl-btn--ghost:hover { background: var(--nvl-card-hi); }
.nvl-btn--lg { min-height: 52px; padding-inline: 1.5rem; font-size: 1rem; }
.nvl-btn[disabled] { opacity: .5; cursor: not-allowed; }

/* ── hero ───────────────────────────────────────────────────────────── */

.nvl-hero { position: relative; overflow: hidden; padding-block: clamp(2.5rem, 8vw, 5rem) var(--nvl-gap); }
.nvl-hero::before {
  content: '';
  position: absolute;
  inset-block-start: -40%;
  inset-inline-start: 50%;
  width: min(120vw, 1100px);
  aspect-ratio: 1;
  transform: translateX(50%);
  background: radial-gradient(circle, color-mix(in srgb, #7c3aed 18%, transparent) 0%, transparent 62%);
  pointer-events: none;
  z-index: 0;
}
.nvl-hero__in { position: relative; z-index: 1; display: grid; gap: 2.5rem; }
@media (min-width: 980px) {
  .nvl-hero__in { grid-template-columns: 1.05fr .95fr; align-items: center; gap: 3rem; }
}
.nvl-hero__copy { display: grid; gap: 1.1rem; justify-items: start; }
.nvl-hero h1 .nvl-mark {
  background: var(--nvl-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ── the address field: the page's one real decision ────────────────── */

.nvl-claim { width: 100%; max-width: 30rem; display: grid; gap: .5rem; }
/* direction: ltr on the BOX, not just on the two text parts.
   With the box left in the page's RTL, flex laid the host at the right and
   the slug to its left, so the address read "my-cafe noriva.ir/" — the two
   halves of one URL in the wrong order. A URL is not Persian text; the
   whole control reads left to right, and the button lands after it. */
.nvl-claim__box {
  direction: ltr;
  display: flex;
  align-items: center;
  gap: .1rem;
  padding: .35rem .35rem .35rem .9rem;
  border: 1.5px solid var(--nvl-line);
  border-radius: 16px;
  background: var(--nv-back-bg-2);
  transition: border-color .2s var(--nv-ease-soft), box-shadow .2s var(--nv-ease-soft);
}
.nvl-claim__box:focus-within {
  border-color: #7c3aed;
  box-shadow: 0 0 0 4px color-mix(in srgb, #7c3aed 16%, transparent);
}
.nvl-claim__box.is-free  { border-color: var(--nv-open); }
.nvl-claim__box.is-taken { border-color: var(--nv-closed); }

/* LTR inside an RTL page: a URL is not Persian text and must not be
   reordered by the bidi algorithm. */
.nvl-claim__host {
  direction: ltr;
  flex: none;
  color: var(--nvl-dim);
  font-size: .95rem;
  font-weight: 700;
  user-select: none;
}
.nvl-claim__in {
  direction: ltr;
  text-align: left;
  flex: 1;
  min-width: 0;
  border: 0;
  outline: 0;
  background: none;
  color: var(--nvl-ink);
  font: inherit;
  font-size: .95rem;
  font-weight: 700;
  padding: .5rem .3rem;
}
.nvl-claim__in::placeholder { color: var(--nvl-dim); font-weight: 500; }
.nvl-claim__go { flex: none; }

/* Below ~400px the host, the field and the button cannot share a line
   without squeezing the field down to about four visible characters. Wrap
   instead: the button keeps its label and the field keeps its width. */
@media (max-width: 400px) {
  .nvl-claim__box { flex-wrap: wrap; padding-inline: .6rem; }
  .nvl-claim__in { flex: 1 1 8rem; }
  .nvl-claim__go { width: 100%; margin-block-start: .4rem; }
}
.nvl-claim__note {
  display: flex;
  align-items: center;
  gap: .35rem;
  min-height: 1.2rem;
  font-size: .78rem;
  font-weight: 700;
  color: var(--nvl-dim);
}
.nvl-claim__note.is-free  { color: var(--nv-open); }
.nvl-claim__note.is-taken { color: var(--nv-closed); }

/* ── swapping one label for another, without touching the DOM ────────
   Font Awesome runs in SVG-with-JS mode: it REPLACES every <i class="fa-*">
   with a freshly built <svg>. Anything holding a reference to the original
   <i> — Alpine's x-if and x-show both do — is then pointing at a node that
   is no longer in the tree, so removing it removes nothing and the "wrong"
   icon stays on screen. (Measured: the free tick survived a switch to
   taken.) So both states stay in the DOM permanently and a data attribute
   on a plain wrapper, which Font Awesome has no reason to touch, decides
   which one is displayed. */
.nvl-swap > span { display: none; align-items: center; gap: .45rem; }
.nvl-swap[data-on="a"] > .nvl-swap__a,
.nvl-swap[data-on="free"] > .nvl-swap__a,
.nvl-swap[data-on="b"] > .nvl-swap__b,
.nvl-swap[data-on="taken"] > .nvl-swap__b { display: inline-flex; }
.nvl-claim__ico:empty { display: none; }

.nvl-trust { display: flex; flex-wrap: wrap; gap: .5rem 1.1rem; font-size: .8rem; color: var(--nvl-dim); }
.nvl-trust span { display: inline-flex; align-items: center; gap: .35rem; }

/* ── the phone mock ─────────────────────────────────────────────────── */
/* Holds a live iframe of /noriva, not a picture of one. The trick that makes
   it look right: the card renders at a REAL phone width and is then scaled
   down to fit, rather than being handed 280px and re-laying itself out. A
   card squeezed to 280px would trip its own narrow-screen rules and show a
   layout no actual visitor ever sees, which is a strange thing for a hero
   whose whole claim is "this is what you get". */

.nvl-shot { justify-self: center; width: min(300px, 82vw); display: grid; gap: .7rem; }
.nvl-phone {
  position: relative;
  aspect-ratio: 9 / 18.5;
  border-radius: 34px;
  padding: 9px;
  background: linear-gradient(160deg, #2a2f3a, #12141a);
  box-shadow: 0 40px 70px -30px rgba(0, 0, 0, .6), 0 0 0 1px rgba(255, 255, 255, .06) inset;
}
.nvl-phone__screen {
  position: relative;
  width: 100%; height: 100%;
  border-radius: 26px;
  overflow: hidden;
  background: var(--nv-back-bg);
}

/* --nvl-live-w is the width the CARD believes it has. 390px is a common
   phone and comfortably above the card's own breakpoints. The scale factor
   is derived from it in the media queries below, so the two can never drift
   apart the way two hand-tuned numbers do. */
.nvl-phone__live {
  position: absolute;

  /* PHYSICAL left/top, not inset-inline-start.
     The page is RTL, so inset-inline-start resolves to `right: 0` — which
     pinned the 390px-wide box by its RIGHT edge, putting its left edge at
     -108px, and `transform-origin: top left` then scaled about that point.
     Measured: the card sat 108px outside its own bezel with a third of it
     clipped away. The transform origin and the pinned corner have to be the
     same corner, and the simplest way to guarantee that is to name it
     physically. */
  top: 0;
  left: 0;

  /* Exactly 1/scale of the screen in both directions, so scaling by that
     factor lands on precisely 100% × 100%. Deriving both from one variable
     is what keeps them in step: the earlier version fixed the width at
     390px and computed the height from the phone's 9/18.5 aspect, but that
     aspect belongs to the OUTER box including its 9px bezel, so the inner
     screen is a slightly different shape and the card came up 19px short. */
  width: calc(100% / var(--nvl-live-s, 1));
  height: calc(100% / var(--nvl-live-s, 1));

  border: 0;
  display: block;
  transform-origin: top left;
  transform: scale(var(--nvl-live-s, 1));
  background: var(--nv-back-bg);

  /* Decorative. The card page has nothing to scroll, so an interactive embed
     could not steal the page's scroll — but it could still swallow a tap
     meant for the mock, and a visitor poking at a shrunken card is not the
     thing the hero is asking them to do. The overlay below takes the taps
     and opens the real page instead. */
  pointer-events: none;
}

/* Covers the whole mock, so a tap anywhere on the phone opens the card in
   its own page rather than doing nothing. */
.nvl-phone__open {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: 26px;
  display: block;
}
.nvl-phone__open:focus-visible {
  outline: 2px solid #7c3aed;
  outline-offset: 3px;
}

/* The scale is the screen's real width over --nvl-live-w. This default
   covers the common case — a 300px mock less the 9px bezel each side, over
   390 — and nv-landing.js overwrites it with the measured ratio, which is
   what makes it exact on a 320px phone where the mock is 82vw instead.

   Deliberately NOT a media query doing calc((82vw - 18px) / 390px): CSS
   cannot divide one length by another, so that expression is invalid and
   the whole declaration is dropped, silently, leaving the iframe at full
   size behind a clipping mask. The JS is the honest way to get a ratio. */
.nvl-shot { --nvl-live-w: 390px; --nvl-live-s: calc(282 / 390); }

.nvl-shot__open {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .4rem;
  font-size: .8rem;
  font-weight: 700;
  color: var(--nvl-dim);
  text-decoration: none;
}
.nvl-shot__open:hover { color: var(--nvl-ink); }

/* ── who it is for ──────────────────────────────────────────────────── */
/* Real business types out of the database, so a trade added in the admin
   shows up here. Fixed tile size for every one of them: a row of logos at
   their natural sizes reads as a clip-art collage, and the whole point of
   this strip is that it looks like a considered list of what the product
   supports. */

.nvl-logos { padding-block: clamp(2rem, 5vw, 3.2rem) !important; }
.nvl-logos__cap {
  text-align: center;
  font-size: .8rem;
  font-weight: 700;
  color: var(--nvl-dim);
  margin-bottom: 1.4rem !important;
}
.nvl-types {
  margin: 0;
  padding: 0;
  list-style: none;
  /* Wrapping flex, not a grid.
     A grid packs each row from the start edge, so thirteen tiles at nine per
     row left the second row of four hanging off one side under a gap — which
     reads as a layout that ran out rather than a list that wrapped. Centred
     flex rows stay balanced at any count, and the count here is whatever the
     business_types table happens to hold. */
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1.3rem 1rem;
  max-width: 56rem;
  margin-inline: auto;
}
.nvl-type {
  flex: 0 0 84px;
  display: grid;
  justify-items: center;
  align-content: start;
  gap: .5rem;
  text-align: center;
}
.nvl-type__ico {
  width: 52px; height: 52px;
  display: grid;
  place-items: center;
  border-radius: 15px;
  border: 1px solid var(--nvl-line);
  background: var(--nvl-card);
  overflow: hidden;
}
.nvl-type__ico img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: 7px;
  box-sizing: border-box;
  filter: brightness(0) invert(1);
}
/* The fallback for a type with no icon uploaded, and for the "+N" tile. */
.nvl-type__ico--mono {
  font-weight: 900;
  font-size: 1.15rem;
  color: #7c3aed;
  background: var(--nvl-card-hi);
  font-variant-numeric: tabular-nums;
}
.nvl-type__t {
  font-size: .78rem;
  font-weight: 700;
  color: var(--nvl-dim);
  line-height: 1.5;
}
.nvl-type--more .nvl-type__t { color: var(--nvl-dim); }

/* ── the three products ─────────────────────────────────────────────── */

.nvl-grid { display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 760px) { .nvl-grid--3 { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 760px) { .nvl-grid--2 { grid-template-columns: repeat(2, 1fr); } }

.nvl-card {
  display: grid;
  gap: .6rem;
  align-content: start;
  padding: 1.4rem;
  border: 1px solid var(--nvl-line);
  border-radius: var(--nvl-r);
  background: var(--nvl-card);
  transition: transform .25s var(--nv-ease-soft), border-color .25s var(--nv-ease-soft),
              box-shadow .25s var(--nv-ease-soft);
}
.nvl-card:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, #7c3aed 45%, var(--nvl-line));
  box-shadow: 0 22px 40px -28px rgba(0, 0, 0, .6);
}
.nvl-card__ico {
  width: 44px; height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 13px;
  background: var(--nvl-grad);
  color: #fff;
  font-size: 17px;
}
.nvl-card p { color: var(--nvl-dim); font-size: .88rem; }
.nvl-card ul { margin: .3rem 0 0; padding: 0; list-style: none; display: grid; gap: .3rem; }
.nvl-card li { font-size: .83rem; color: var(--nvl-dim); display: flex; gap: .45rem; align-items: baseline; }
.nvl-card li::before { content: '—'; color: #7c3aed; flex: none; }

/* ── image slots ────────────────────────────────────────────────────
   Placeholders that describe what belongs in them.

   The alternative — leaving the space empty until art exists — means the
   layout is designed twice: once without pictures and again, differently,
   when they arrive. This reserves the exact box, so dropping a file in
   changes nothing but the pixels. Each one carries the prompt it was
   specified with, visible on the page, so whoever generates the art is
   reading the brief rather than guessing from a filename.

   Filling one in: put the file under app/static/app/img/landing/ and set
   `src` for that key in LANDING_IMAGES (noriva/views.py). The box, its
   aspect ratio and its alt text stay exactly as they are. */

.nvl-fig { margin: 0; }
.nvl-fig img {
  display: block;
  width: 100%;
  /* height comes from the aspect-ratio class the placeholder also uses, so
     the box is identical whether art is in it or not — which is the whole
     point of reserving it. `height: 100%` would fight that ratio. */
  height: auto;
  object-fit: cover;
  border-radius: 14px;
}
.nvl-ph {
  display: grid;
  /* start, not center: with overflow the centred content scrolled to its
     middle, clipping the "جای تصویر" tag off the top and the end of the
     prompt off the bottom. Anchored to the top, the label is always the
     first thing visible and only the tail of a long prompt scrolls. */
  align-content: start;
  justify-items: center;
  gap: .45rem;
  padding: .85rem;
  /* The box must be the FINAL box, or reserving it achieves nothing. Long
     prompts were stretching it past its aspect ratio — measured, the three
     product placeholders came out 304, 267 and 248px tall purely because
     their briefs are different lengths, which is not a thing the finished
     page will ever do. The prompt scrolls inside instead. */
  overflow: auto;
  overscroll-behavior: contain;
  border: 1.5px dashed color-mix(in srgb, #7c3aed 40%, var(--nvl-line));
  border-radius: 14px;
  background:
    repeating-linear-gradient(135deg,
      transparent 0 10px,
      color-mix(in srgb, #7c3aed 5%, transparent) 10px 20px),
    var(--nvl-card-hi);
  text-align: center;
}
.nvl-ph__tag {
  justify-self: center;
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: .15rem .6rem;
  border-radius: 999px;
  background: var(--nvl-grad);
  color: #fff;
  font-size: .66rem;
  font-weight: 800;
}
.nvl-ph__name { font-size: .84rem; font-weight: 800; color: var(--nvl-ink); }
/* The prompt is English and is meant to be copied into an image tool, so it
   is LTR and monospaced regardless of the page's direction. */
.nvl-ph__prompt {
  direction: ltr;
  text-align: left;
  max-width: 46ch;
  margin-inline: auto;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .68rem;
  line-height: 1.7;
  color: var(--nvl-dim);
  white-space: pre-wrap;
}
.nvl-ph--wide { aspect-ratio: 16 / 6; }
.nvl-ph--card { aspect-ratio: 16 / 10; }
@media (max-width: 640px) {
  .nvl-ph--wide { aspect-ratio: 4 / 3; }
  .nvl-ph__prompt { font-size: .63rem; }
}

/* ── steps ──────────────────────────────────────────────────────────── */

.nvl-steps__list { display: grid; gap: 1rem; counter-reset: nvl-step; }
@media (min-width: 760px) { .nvl-steps__list { grid-template-columns: repeat(3, 1fr); } }
.nvl-step {
  position: relative;
  padding: 1.4rem 1.4rem 1.4rem 1.4rem;
  border: 1px solid var(--nvl-line);
  border-radius: var(--nvl-r);
  background: var(--nvl-card);
  counter-increment: nvl-step;
}
.nvl-step::before {
  /* `persian` is a CSS counter style: ۱ ۲ ۳ rather than 1 2 3. A page whose
     prices, dates and character counts are all in Persian digits should not
     number its own steps in Latin ones. Browsers without the style fall
     back to decimal, which is the old behaviour and not a failure. */
  content: counter(nvl-step, persian);
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  margin-bottom: .7rem;
  border-radius: 11px;
  background: var(--nvl-grad);
  color: #fff;
  font-weight: 900;
  font-size: .95rem;
  font-variant-numeric: tabular-nums;
}
.nvl-step p { color: var(--nvl-dim); font-size: .88rem; margin-top: .35rem; }

/* ── pricing ────────────────────────────────────────────────────────── */

/* Flex column, not grid: the cards hold different numbers of bullets, and
   the "choose this plan" buttons have to line up across the row rather than
   sitting wherever each card's list happens to end. margin-block-start:auto
   on the button is what does it, and that needs a flex parent. */
.nvl-plan {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: .8rem;
  padding: 1.5rem;
  border: 1px solid var(--nvl-line);
  border-radius: var(--nvl-r);
  background: var(--nvl-card);
}
.nvl-plan > .nvl-btn { margin-block-start: auto; }
.nvl-plan.is-featured {
  border-color: transparent;
  background:
    linear-gradient(var(--nvl-card), var(--nvl-card)) padding-box,
    var(--nvl-grad) border-box;
  border: 1.5px solid transparent;
  box-shadow: 0 24px 46px -30px #4f46e5;
}
.nvl-plan__tag {
  position: absolute;
  inset-block-start: -11px;
  inset-inline-end: 1.2rem;
  padding: .18rem .6rem;
  border-radius: 999px;
  background: var(--nvl-grad);
  color: #fff;
  font-size: .68rem;
  font-weight: 800;
}
.nvl-plan__price { display: flex; align-items: baseline; gap: .4rem; flex-wrap: wrap; }
.nvl-plan__now { font-size: 1.75rem; font-weight: 900; font-variant-numeric: tabular-nums; }
.nvl-plan__was { font-size: .85rem; color: var(--nvl-dim); text-decoration: line-through; }
.nvl-plan__unit { font-size: .78rem; color: var(--nvl-dim); font-weight: 700; }
.nvl-plan__desc { font-size: .85rem; color: var(--nvl-dim); }
.nvl-plan__list { margin: 0; padding: 0; list-style: none; display: grid; gap: .35rem; }
.nvl-plan__list li {
  font-size: .84rem;
  display: flex;
  gap: .45rem;
  align-items: baseline;
}
.nvl-plan__list li svg,
.nvl-plan__list li i { color: #7c3aed; flex: none; }
.nvl-plan__off {
  padding: .1rem .45rem;
  border-radius: 6px;
  background: var(--nv-closed);
  color: #fff;
  font-size: .7rem;
  font-weight: 800;
}

.nvl-cta-row { margin-block-start: 2rem; display: flex; gap: .7rem; flex-wrap: wrap; }
.nvl-opt { font-weight: 500; }
.nvl-u { text-decoration: underline; }

/* ── FAQ ────────────────────────────────────────────────────────────── */

.nvl-faq__list { display: grid; gap: .6rem; max-width: 52rem; }
.nvl-q {
  border: 1px solid var(--nvl-line);
  border-radius: 15px;
  background: var(--nvl-card);
  overflow: hidden;
}
.nvl-q > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.2rem;
  cursor: pointer;
  font-weight: 700;
  font-size: .95rem;
  list-style: none;
}
.nvl-q > summary::-webkit-details-marker { display: none; }
.nvl-q > summary::after {
  content: '+';
  flex: none;
  font-size: 1.2rem;
  font-weight: 400;
  color: var(--nvl-dim);
  transition: transform .2s var(--nv-ease-soft);
}
.nvl-q[open] > summary::after { transform: rotate(45deg); }
.nvl-q__a { padding: 0 1.2rem 1.1rem; color: var(--nvl-dim); font-size: .88rem; }

/* ── contact ────────────────────────────────────────────────────────── */

.nvl-contact__grid { display: grid; gap: 1.6rem; }
@media (min-width: 860px) { .nvl-contact__grid { grid-template-columns: 1fr 1fr; gap: 2.5rem; } }

.nvl-form { display: grid; gap: .8rem; }
.nvl-field { display: grid; gap: .35rem; }
.nvl-field > label { font-size: .82rem; font-weight: 700; color: var(--nvl-dim); }
.nvl-input, .nvl-textarea {
  width: 100%;
  box-sizing: border-box;
  padding: .75rem .9rem;
  border: 1px solid var(--nvl-line);
  border-radius: 13px;
  background: var(--nv-back-bg-2);
  color: var(--nvl-ink);
  font: inherit;
  font-size: .9rem;
  transition: border-color .18s var(--nv-ease-soft), box-shadow .18s var(--nv-ease-soft);
}
.nvl-textarea { min-height: 130px; resize: vertical; line-height: 1.9; }
.nvl-input:focus, .nvl-textarea:focus {
  outline: 0;
  border-color: #7c3aed;
  box-shadow: 0 0 0 4px color-mix(in srgb, #7c3aed 14%, transparent);
}
.nvl-form__note { font-size: .76rem; color: var(--nvl-dim); }
.nvl-form__note.is-bad { color: var(--nv-closed); font-weight: 700; }
.nvl-form__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .8rem;
  flex-wrap: wrap;
}
/* The counter is a number that changes as you type; tabular figures stop
   the line jittering sideways on every keystroke. */
.nvl-count { font-variant-numeric: tabular-nums; }
.nvl-form__msg { font-size: .84rem; font-weight: 700; min-height: 1.2rem; }
.nvl-form__msg.is-ok  { color: var(--nv-open); }
.nvl-form__msg.is-bad { color: var(--nv-closed); }

.nvl-ways { display: grid; gap: .7rem; align-content: start; }
.nvl-way {
  display: flex;
  align-items: center;
  gap: .8rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--nvl-line);
  border-radius: 15px;
  background: var(--nvl-card);
  color: var(--nvl-ink);
  text-decoration: none;
  transition: background-color .2s var(--nv-ease-soft);
}
.nvl-way:hover { background: var(--nvl-card-hi); }
.nvl-way__ico {
  width: 38px; height: 38px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 11px;
  background: var(--nvl-card-hi);
  color: #7c3aed;
}
.nvl-way b { display: block; font-size: .9rem; }
.nvl-way small { color: var(--nvl-dim); font-size: .78rem; }

/* ── footer ─────────────────────────────────────────────────────────── */

.nvl-foot { padding-block: 2.5rem; border-top: 1px solid var(--nvl-line); }
.nvl-foot__in { display: grid; gap: 1.4rem; }
@media (min-width: 760px) { .nvl-foot__in { grid-template-columns: 1.4fr 1fr 1fr; } }
.nvl-foot h4 { margin: 0 0 .5rem; font-size: .85rem; font-weight: 800; }
.nvl-foot ul { margin: 0; padding: 0; list-style: none; display: grid; gap: .35rem; }
.nvl-foot a { color: var(--nvl-dim); font-size: .84rem; text-decoration: none; }
.nvl-foot a:hover { color: var(--nvl-ink); }
.nvl-foot__brand { margin-bottom: .7rem; }
.nvl-foot__blurb { font-size: .84rem; color: var(--nvl-dim); max-width: 38ch; }
.nvl-foot__seal { margin-block-start: 1rem; }
.nvl-foot__seal img { max-width: 110px; height: auto; cursor: pointer; }
.nvl-foot__legal {
  margin-top: 1.6rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--nvl-line);
  display: flex;
  flex-wrap: wrap;
  gap: .6rem 1.2rem;
  align-items: center;
  justify-content: space-between;
  color: var(--nvl-dim);
  font-size: .78rem;
}

/* ── a sticky action on phones ──────────────────────────────────────── */
/* The hero field scrolls away; on a phone the decision has to stay within
   thumb reach or it is only available to whoever scrolls back up. */
.nvl-dock {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: 45;
  padding: .7rem var(--nvl-pad) max(.7rem, env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--nv-back-bg) 92%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-top: 1px solid var(--nvl-line);
  transform: translateY(110%);
  transition: transform .28s var(--nv-ease-soft);
}
.nvl-dock.is-on { transform: none; }
.nvl-dock .nvl-btn { width: 100%; }
@media (min-width: 900px) { .nvl-dock { display: none; } }

/* ── entrance ───────────────────────────────────────────────────────── */

/* Scoped to the attribute the page's head script sets, so the hidden state
   only ever exists where scripting is live to undo it. Without the guard a
   failed script leaves every section at opacity 0 — an invisible page. */
[data-nvl-anim] .nvl-rise { opacity: 0; transform: translateY(14px); }
.nvl-rise.is-in {
  opacity: 1;
  transform: none;
  transition: opacity .55s var(--nv-ease-soft), transform .55s var(--nv-ease-soft);
}
@media (prefers-reduced-motion: reduce) {
  [data-nvl-anim] .nvl-rise,
  .nvl-rise, .nvl-rise.is-in { opacity: 1; transform: none; transition: none; }
  .nvl-card:hover { transform: none; }
  .nvl-dock { transition: none; }
}

/* Nothing on this page may scroll sideways. */
.nvl { overflow-x: hidden; }
